//------------------------------------------------------------------------------
// <copyright file="BaseConfigurationRecord.cs" company="Microsoft">
//     
//      Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
//     
//      The use and distribution terms for this software are contained in the file
//      named license.txt, which can be found in the root of this distribution.
//      By using this software in any fashion, you are agreeing to be bound by the
//      terms of this license.
//     
//      You must not remove this notice, or any other, from this software.
//     
// </copyright>
//------------------------------------------------------------------------------

namespace System.Configuration {
    using System.Configuration.Internal;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Security.Permissions;
    using System.Security;
    using System.Text;
    using System.Xml;

    //
    // This object represents the configuration for a request path, and is cached per-path.
    //
    abstract internal class BaseConfigurationRecord : IInternalConfigRecord {
        protected const string NL                                           = "\r\n";
        protected const string KEYWORD_TRUE                                 = "true";
        protected const string KEYWORD_FALSE                                = "false";
        protected const string KEYWORD_CONFIGURATION                        = "configuration";
        protected const string KEYWORD_CONFIGURATION_NAMESPACE              = "http://schemas.microsoft.com/.NetConfiguration/v2.0";
        protected const string KEYWORD_CONFIGSECTIONS                       = "configSections";
        protected const string KEYWORD_SECTION                              = "section";
        protected const string KEYWORD_SECTION_NAME                         = "name";
        protected const string KEYWORD_SECTION_TYPE                         = "type";
        protected const string KEYWORD_SECTION_ALLOWLOCATION                = "allowLocation";
        protected const string KEYWORD_SECTION_ALLOWDEFINITION              = "allowDefinition";
        protected const string KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE   = "Everywhere";
        protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY  = "MachineOnly";
        protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION  = "MachineToApplication";
        protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT  = "MachineToWebRoot";
        protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION           = "allowExeDefinition";
        protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING = "MachineToRoamingUser";
        protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL   = "MachineToLocalUser";
        protected const string KEYWORD_SECTION_RESTARTONEXTERNALCHANGES     = "restartOnExternalChanges";
        protected const string KEYWORD_SECTION_REQUIREPERMISSION            = "requirePermission";
        protected const string KEYWORD_SECTIONGROUP                         = "sectionGroup";
        protected const string KEYWORD_SECTIONGROUP_NAME                    = "name";
        protected const string KEYWORD_SECTIONGROUP_TYPE                    = "type";
        protected const string KEYWORD_REMOVE                               = "remove";
        protected const string KEYWORD_CLEAR                                = "clear";
        protected const string KEYWORD_LOCATION                             = "location";
        protected const string KEYWORD_LOCATION_PATH                        = "path";
        protected const string KEYWORD_LOCATION_ALLOWOVERRIDE               = "allowOverride";
        protected const string KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS  = "inheritInChildApplications";
        protected const string KEYWORD_CONFIGSOURCE                         = "configSource";
        protected const string KEYWORD_XMLNS                                = "xmlns";
        internal  const string KEYWORD_PROTECTION_PROVIDER                  = "configProtectionProvider";

        protected const string FORMAT_NEWCONFIGFILE                 = "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";
        protected const string FORMAT_CONFIGURATION                 = "<configuration>\r\n";
        protected const string FORMAT_CONFIGURATION_NAMESPACE       = "<configuration xmlns=\"{0}\">\r\n";
        protected const string FORMAT_CONFIGURATION_ENDELEMENT      = "</configuration>";

        protected const string FORMAT_LOCATION_NOPATH               = "<location allowOverride=\"{0}\" inheritInChildApplications=\"{1}\">\r\n";
        protected const string FORMAT_LOCATION_PATH                 = "<location path=\"{2}\" allowOverride=\"{0}\" inheritInChildApplications=\"{1}\">\r\n";
        protected const string FORMAT_LOCATION_ENDELEMENT           = "</location>";

        protected const string FORMAT_SECTION_CONFIGSOURCE          = "<{0} configSource=\"{1}\" />";
        protected const string FORMAT_CONFIGSOURCE_FILE             = "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";

        protected const string FORMAT_SECTIONGROUP_ENDELEMENT       = "</sectionGroup>";

        // Class flags should only be used with the ClassFlags property.
        protected const int ClassSupportsChangeNotifications    = 0x00000001;
        protected const int ClassSupportsRefresh                = 0x00000002;
        protected const int ClassSupportsImpersonation          = 0x00000004;
        protected const int ClassSupportsRestrictedPermissions  = 0x00000008;
        protected const int ClassSupportsKeepInputs             = 0x00000010;
        protected const int ClassSupportsDelayedInit            = 0x00000020;
        protected const int ClassIgnoreLocalErrors              = 0x00000040;

        // Flags to use with the _flags field.
        protected const int ProtectedDataInitialized            = 0x00000001;
        protected const int Closed                              = 0x00000002;
        protected const int PrefetchAll                         = 0x00000008;
        protected const int IsAboveApplication                  = 0x00000020;
        private   const int ContextEvaluated                    = 0x00000080;
        private   const int IsLocationListResolved              = 0x00000100;
        protected const int NamespacePresentInFile              = 0x00000200;


        private   const int RestrictedPermissionsResolved       = 0x00000800;

        protected const int IsTrusted                           = 0x00002000;

        protected const int SupportsChangeNotifications         = 0x00010000;
        protected const int SupportsRefresh                     = 0x00020000;
        protected const int SupportsPath                        = 0x00040000;
        protected const int SupportsKeepInputs                  = 0x00080000;
        protected const int SupportsLocation                    = 0x00100000;

        // Flags for Mgmt Configuration Record
        protected const int ForceLocationWritten                = 0x01000000;
        protected const int SuggestLocationRemoval              = 0x02000000;
        protected const int NamespacePresentCurrent             = 0x04000000;

        internal const char             ConfigPathSeparatorChar = '/';
        internal const string           ConfigPathSeparatorString = "/";
        static internal readonly char[] ConfigPathSeparatorParams = new char[] {ConfigPathSeparatorChar};

        private static ConfigurationPermission  s_unrestrictedConfigPermission; // cached ConfigurationPermission

        protected SafeBitVector32               _flags;                 // state
        protected BaseConfigurationRecord       _parent;                // parent record
        protected Hashtable                     _children;              // configName -> record
        protected InternalConfigRoot            _configRoot;            // root of configuration            
        protected string                        _configName;            // the last part of the config path 
        protected string                        _configPath;            // the full config path             
        protected string                        _locationSubPath;       // subPath for the config record when editing a location configuration

        private ConfigRecordStreamInfo          _configStreamInfo;      // stream info for the config record

        private object                          _configContext;         // Context for config level
        private ProtectedConfigurationSection   _protectedConfig;       // section containing the encryption providers
        private PermissionSet                   _restrictedPermissions; // cached restricted permission set
        private ConfigurationSchemaErrors       _initErrors;            // errors encountered during the parse of the configuration file
        private BaseConfigurationRecord         _initDelayedRoot;       // root of delayed initialization

        // Records information about <configSections> present in this web.config file.
        //      config key -> FactoryRecord
        protected Hashtable                     _factoryRecords;

        // Records information about sections that apply to this path,
        // which may be found in this web.config file, in a parent through
        // inheritance, or in a parent through <location>
        //      config key -> SectionRecord
        protected Hashtable                     _sectionRecords;

        // Records information about sections in a <location> directive
        // that do not apply to this configPath (sections where path != ".")
        protected ArrayList                     _locationSections;


        internal BaseConfigurationRecord() {
            // not strictly necessary, but compiler spits out a warning without this initiailization
            _flags = new SafeBitVector32();
        }

        // Class flags
        protected abstract SimpleBitVector32 ClassFlags {get;}

        // Create the factory that will evaluate configuration
        protected abstract object CreateSectionFactory(FactoryRecord factoryRecord);

        // Create the configuration object
        protected abstract object CreateSection(bool inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentConfig, ConfigXmlReader reader);

        // Use the parent result in creating the child
        protected abstract object UseParentResult(string configKey, object parentResult, SectionRecord sectionRecord);

        // Return the runtime object from GetSection
        protected abstract object GetRuntimeObject(object result);

        //
        // IInternalConfigRecord methods
        //

        public string ConfigPath {
            get {return _configPath;}
        }

        public string StreamName {
            get {return ConfigStreamInfo.StreamName;}
        }

        public bool HasInitErrors {
            get {
                return _initErrors.HasErrors(ClassFlags[ClassIgnoreLocalErrors]);
            }
        }

        public void ThrowIfInitErrors() {
            ThrowIfParseErrors(_initErrors);
        }

        public object GetSection(string configKey) {
#if DBG
            // On debug builds, the config system depends on system.diagnostics,
            // so we must always return a valid result and never throw.
            if (configKey == "system.diagnostics" && !ClassFlags[ClassIgnoreLocalErrors]) {
                return GetSection(configKey, true, true);
            }
            else {
                return GetSection(configKey, false, true);
            }
#else

            return GetSection(configKey, false, true);
#endif
        }

        public object GetLkgSection(string configKey) {
            return GetSection(configKey, true, true);
        }

        public void RefreshSection(string configKey) {
            _configRoot.ClearResult(this, configKey, true);
        }

        public void Remove() {
            _configRoot.RemoveConfigRecord(this);
        }

        //
        // end of IInternalConfigRecord methods
        //

        internal bool HasStream {
            get { return ConfigStreamInfo.HasStream; }
        }

        // Determine which sections should be prefetched during the first scan.
        private bool ShouldPrefetchRawXml(FactoryRecord factoryRecord) {
            if (_flags[PrefetchAll])
                return true;

            switch (factoryRecord.ConfigKey) {
                case BaseConfigurationRecord.RESERVED_SECTION_PROTECTED_CONFIGURATION:
                case "system.diagnostics":
                case "appSettings":
                case "connectionStrings":
                    return true;
            }

            return Host.PrefetchSection(factoryRecord.Group, factoryRecord.Name);
        }

        protected IDisposable Impersonate() {
            IDisposable context = null;
            if (ClassFlags[ClassSupportsImpersonation]) {
                context = Host.Impersonate();                
            }

            if (context == null) {
                context = EmptyImpersonationContext.GetStaticInstance();
            }

            return context;
        }

        internal PermissionSet GetRestrictedPermissions() {
            if (!_flags[RestrictedPermissionsResolved]) {
                PermissionSet restrictedPermissions;
                bool isHostReady;

                Host.GetRestrictedPermissions(this, out restrictedPermissions, out isHostReady);
                if (isHostReady) {
                    _restrictedPermissions = restrictedPermissions;
                    _flags[RestrictedPermissionsResolved] = true;
                }
            }

            return _restrictedPermissions;
        }

        internal void Init(
                IInternalConfigRoot         configRoot,
                BaseConfigurationRecord     parent,
                string                      configPath,
                string                      locationSubPath) {

            _initErrors = new ConfigurationSchemaErrors();

            //
            // try/catch here is only for unexpected exceptions due to errors in
            // our own code, as we always want the configuration record to be 
            // usable
            //
            try {
                _configRoot = (InternalConfigRoot) configRoot;
                _parent = parent;
                _configPath = configPath;
                _locationSubPath = locationSubPath;
                _configName = ConfigPathUtility.GetName(configPath);

                if (IsLocationConfig) {
                    _configStreamInfo = _parent.ConfigStreamInfo;
                }
                else {
                    _configStreamInfo = new ConfigRecordStreamInfo();
                }

                // no more initialization in case of root config
                if (IsRootConfig)
                    return;

                // determine what features we support
                _flags[SupportsChangeNotifications] = ClassFlags[ClassSupportsChangeNotifications] && Host.SupportsChangeNotifications;
                _flags[SupportsRefresh] = ClassFlags[ClassSupportsRefresh] && Host.SupportsRefresh;
                _flags[SupportsKeepInputs] = ClassFlags[ClassSupportsKeepInputs] || _flags[SupportsRefresh];
                _flags[SupportsPath] = Host.SupportsPath;
                _flags[SupportsLocation] = Host.SupportsLocation;

                // get static state based on the configPath
                if (_flags[SupportsLocation]) {
                    _flags[IsAboveApplication] = Host.IsAboveApplication(_configPath);
                }

                _flags[IsTrusted] = Host.IsTrustedConfigPath(_configPath);

                ArrayList locationSubPathInputs = null;

                if (_flags[SupportsLocation]) {
                    //
                    // Treat location inputs from parent record
                    // as though they were bonafide sections in this record.
                    //
                    if (IsLocationConfig && _parent._locationSections != null) {
                        // Resolve paths and check for errors in location sections.
                        _parent.ResolveLocationSections();

                        int i = 0;
                        while (i < _parent._locationSections.Count) {
                            LocationSectionRecord locationSectionRecord = (LocationSectionRecord) _parent._locationSections[i];

                            if (!StringUtil.EqualsIgnoreCase(locationSectionRecord.SectionXmlInfo.SubPath, _locationSubPath)) {
                                i++;
                            }
                            else {
                                // remove the locationSectionRecord from the list
                                _parent._locationSections.RemoveAt(i);

                                if (locationSubPathInputs == null) {
                                    locationSubPathInputs = new ArrayList();
                                }

                                locationSubPathInputs.Add(locationSectionRecord);
                            }
                        }
                    }

                    //
                    // Add location inputs that apply to this path, all the way up the parent hierarchy.
                    //
                    if (Host.IsLocationApplicable(_configPath)) {
                        BaseConfigurationRecord current = _parent;
                        while (!current.IsRootConfig) {
                            if (current._locationSections != null) {
                                current.ResolveLocationSections();
                                foreach (LocationSectionRecord locationSectionRecord in current._locationSections) {
                                    // We use the location tag input if:
                                    // - The path matches, and
                                    // - It's not skipped due to inheritInChildApplications setting.
                                    if (StringUtil.EqualsIgnoreCase(locationSectionRecord.SectionXmlInfo.TargetConfigPath, this._configPath) &&
                                        !ShouldSkipDueToInheritInChildApplications(locationSectionRecord.SectionXmlInfo.SkipInChildApps)) {
                                        // add the location input for this section
                                        SectionRecord sectionRecord = EnsureSectionRecord(locationSectionRecord.ConfigKey, true);
                                        SectionInput sectionInput = new SectionInput(
                                                locationSectionRecord.SectionXmlInfo, locationSectionRecord.ErrorsList);

                                        sectionRecord.AddLocationInput(sectionInput);

                                        // copy the initialization errors to the record
                                        if (locationSectionRecord.HasErrors) {
                                            this._initErrors.AddSavedLocalErrors(locationSectionRecord.Errors);
                                        }
                                    }
                                }
                            }
    
                            current = current._parent;
                        }
                    }
                }

                if (!IsLocationConfig) {
                    //
                    // If config file exists, open it and parse it once so we can
                    // find what to evaluate later.
                    //
                    InitConfigFromFile();
                }
                else {
                    // Add location sections for this record as bonafide sections.
                    if (locationSubPathInputs != null) {
                        foreach (LocationSectionRecord locationSectionRecord in locationSubPathInputs) {
                            // add the file input
                            SectionRecord sectionRecord = EnsureSectionRecord(locationSectionRecord.ConfigKey, true);
                            SectionInput sectionInput = new SectionInput(locationSectionRecord.SectionXmlInfo, locationSectionRecord.ErrorsList);
                            sectionRecord.AddFileInput(sectionInput);

                            // copy the initialization errors to the record
                            if (locationSectionRecord.HasErrors) {
                                this._initErrors.AddSavedLocalErrors(locationSectionRecord.Errors);
                            }
                        }
                    }
                }
            }
            //
            // Capture all exceptions and include them in initialization errors.
            //
            catch (Exception e) {
                string streamname = (ConfigStreamInfo != null) ? ConfigStreamInfo.StreamName : null;

                _initErrors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, streamname, 0),
                        ExceptionAction.Global);
            }
            catch {
                string streamname = (ConfigStreamInfo != null) ? ConfigStreamInfo.StreamName : null;

                _initErrors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), null, streamname, 0),
                        ExceptionAction.Global);
            }
        }

        // InitConfigFromFile
        //
        // Init the Config From the File.
        //
        private void InitConfigFromFile() {
            bool implicitSectionsAdded = false;

            try {
                // If initialization should be delayed, do not read the file.
                if (ClassFlags[ClassSupportsDelayedInit] && Host.IsInitDelayed(this)) {
                    // determine the root of delayed initialization
                    if (_parent._initDelayedRoot == null) {
                        _initDelayedRoot = this;
                    }
                    else {
                        _initDelayedRoot = _parent._initDelayedRoot;
                    }
                }
                else {
                    //
                    // This parent of a record that is not initDelayed must also
                    // not be initDelayed.
                    //
                    Debug.Assert(!_parent.IsInitDelayed, "!_parent.IsInitDelayed");

                    using (Impersonate()) {

                        //
                        // Get the stream name. Note that this may be an expensive operation 
                        // for the client configuration host, which is why it comes after the 
                        // check for delayed init.
                        //
                        ConfigStreamInfo.StreamName = Host.GetStreamName(_configPath);
                        if (!String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
                            // Lets listen to the stream
                            ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName);
                            using (Stream stream = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) {
                                if (stream == null) {
                                    // There is no stream to load from
                                    return;
                                }

                                ConfigStreamInfo.HasStream = true;

                                // Determine whether or not to prefetch.
                                _flags[PrefetchAll] = Host.PrefetchAll(_configPath, ConfigStreamInfo.StreamName);

                                using (XmlUtil xmlUtil = new XmlUtil(stream, ConfigStreamInfo.StreamName, true, _initErrors)) {
                                    ConfigStreamInfo.StreamEncoding = xmlUtil.Reader.Encoding;

                                    // Read the factories
                                    Hashtable factoryList = ScanFactories(xmlUtil);
                                    _factoryRecords = factoryList;

                                    // Add implicit sections before scanning sections
                                    AddImplicitSections(null);
                                    implicitSectionsAdded = true;

                                    // Read the sections themselves
                                    if (xmlUtil.Reader.Depth == 1) {
                                        ScanSections(xmlUtil);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //
            // Capture all exceptions and include them in _initErrors so execution in Init can continue.
            //
            catch (XmlException e) {
                //
                // Treat invalid XML as unrecoverable by replacing all errors with the XML error.
                //
                _initErrors.SetSingleGlobalError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0));
            }
            catch (Exception e) {
                _initErrors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0),
                        ExceptionAction.Global);
            } 
            catch {
                _initErrors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), null, ConfigStreamInfo.StreamName, 0),
                        ExceptionAction.Global);
            }

            //
            // If there are global errors that make all input invalid, 
            // reset our state so that inherited configuration can still be used, 
            // including location sections that apply to this file.
            //
            if (_initErrors.HasGlobalErrors) {

                //
                // Parsing of a section may have been in progress when the exception
                // was thrown, so clear any accumulated local errors.
                //
                _initErrors.ResetLocalErrors();

                //
                // Stop monitoring any configSource streams, but still continue
                // to monitor the main config file if it was successfully monitored.
                //
                HybridDictionary streamInfos = null;
                lock (this) {
                    if (ConfigStreamInfo.HasStreamInfos) {
                        streamInfos = ConfigStreamInfo.StreamInfos;
                        ConfigStreamInfo.ClearStreamInfos();

                        if (!String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
                            StreamInfo fileStreamInfo = (StreamInfo) streamInfos[ConfigStreamInfo.StreamName];

                            // add this file's streaminfo to the now empty list
                            if (fileStreamInfo != null) {
                                streamInfos.Remove(ConfigStreamInfo.StreamName);
                                ConfigStreamInfo.StreamInfos.Add(ConfigStreamInfo.StreamName, fileStreamInfo);
                            }
                        }
                    }
                }

                // Stop monitoring streams outside the lock, to prevent deadlock.
                if (streamInfos != null) {
                    foreach (StreamInfo streamInfo in streamInfos.Values) {
                        if (streamInfo.IsMonitored) {
                            Host.StopMonitoringStreamForChanges(streamInfo.StreamName, ConfigStreamInfo.CallbackDelegate);
                        }
                    }
                }

                // Remove file input
                if (_sectionRecords != null) {
                    List<SectionRecord> removes = null;
                    foreach (SectionRecord sectionRecord in _sectionRecords.Values) {
                        if (sectionRecord.HasLocationInputs) {
                            // Remove any file input
                            sectionRecord.RemoveFileInput();
                        }
                        else {
                            // Remove the entire section record
                            if (removes == null) {
                                removes = new List<SectionRecord>();                            
                            }

                            removes.Add(sectionRecord);
                        }
                    }

                    if (removes != null) {
                        foreach (SectionRecord sectionRecord in removes) {
                            _sectionRecords.Remove(sectionRecord.ConfigKey);
                        }
                    }
                }

                // Remove all location section input defined here
                if (_locationSections != null) {
                    _locationSections.Clear();
                }

                // Remove all factory records
                if (_factoryRecords != null) {
                    _factoryRecords.Clear();
                }
            }

            if (!implicitSectionsAdded) {
                // Always add implicit sections no matter we have a file or not.
                AddImplicitSections(null);
            }
        }

        private bool IsInitDelayed {
            get {
                return _initDelayedRoot != null;
            }
        }

        // RefreshFactoryRecord
        //
        // Refresh the Factory Record for a particular section.
        //
        private void RefreshFactoryRecord(string configKey) {
            Hashtable                 factoryList   = null;
            FactoryRecord             factoryRecord = null;
            ConfigurationSchemaErrors errors;

            errors = new ConfigurationSchemaErrors();

            // Get Updated Factory List from File
            int lineNumber = 0;
            try {
                using (Impersonate()) {
                    using (Stream stream = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) {
                        if (stream != null) {
                            ConfigStreamInfo.HasStream = true;

                            using (XmlUtil xmlUtil = new XmlUtil(stream, ConfigStreamInfo.StreamName, true, errors)) {
                                try {
                                    factoryList = ScanFactories(xmlUtil);
                                    ThrowIfParseErrors(xmlUtil.SchemaErrors);
                                }
                                catch {
                                    lineNumber = xmlUtil.LineNumber;
                                    throw;
                                }
                            }
                        }
                    }
                }

                // Add implicit sections to the factory list
                if (factoryList == null) {
                    // But if factoryList isn't found in this config, we still try to
                    // add implicit sections to an empty factoryList.
                    factoryList = new Hashtable();
                }
                
                AddImplicitSections(factoryList);

                if (factoryList != null) {
                    factoryRecord = (FactoryRecord) factoryList[configKey];
                }
            }
            //
            // Guarantee that exceptions contain the name of the stream and an approximate line number if available.
            //
            // And don't allow frames up the stack to run exception filters while impersonated.
            catch (Exception e) {
                errors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, lineNumber),
                        ExceptionAction.Global);

            } 
            catch {
                errors.AddError(
                        ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), null, ConfigStreamInfo.StreamName, lineNumber),
                        ExceptionAction.Global);
            }
                    
            // Set/Add/Remove Record
            // Note that the _factoryRecords hashtable is protected by the hierarchy lock.
            if (factoryRecord != null || HasFactoryRecords) {
                EnsureFactories()[configKey] = factoryRecord;
            }
            
            // Throw accumulated errors
            ThrowIfParseErrors(errors);
        }
        
        internal IInternalConfigHost Host {
            get {return _configRoot.Host;}
        }

        internal BaseConfigurationRecord Parent {
            get {return _parent;}
        }

        internal bool IsRootConfig {
            get {return _parent == null;}
        }

        internal bool IsMachineConfig {
            get {return _parent == _configRoot.RootConfigRecord;}
        }


        internal string LocationSubPath {
            get {return _locationSubPath;}
        }

        internal bool IsLocationConfig {
            get {return _locationSubPath != null;}
        }

        protected ConfigRecordStreamInfo ConfigStreamInfo {
            get {
                if (IsLocationConfig) {
                    return _parent._configStreamInfo;
                }
                else {
                    return _configStreamInfo;
                }
            }
        }

        private object GetSection(string configKey, bool getLkg, bool checkPermission) {
            object result;
            object resultRuntimeObject;

            //
            // Note that GetSectionRecursive may invalidate this record, 
            // so there should be no further references to 'this' after the call.
            //
            GetSectionRecursive(
                    configKey, getLkg, checkPermission, true /* getRuntimeObject */, true /* requestIsHere */,
                    out result, out resultRuntimeObject);

            return resultRuntimeObject;
        }

        private void GetSectionRecursive(
                string configKey, bool getLkg, bool checkPermission, bool getRuntimeObject, bool requestIsHere, 
                out object result, out object resultRuntimeObject) {

            result = null;
            resultRuntimeObject = null;

#if DBG
            Debug.Assert(requestIsHere || !checkPermission, "requestIsHere || !checkPermission");
            if (getLkg) {
                Debug.Assert(getRuntimeObject == true, "getRuntimeObject == true");
                Debug.Assert(requestIsHere == true, "requestIsHere == true");
            }
#endif

            //
            // Store results in temporary variables, because we don't want to return
            // results if an exception is thrown by CheckPermissionAllowed.
            //
            object tmpResult = null;
            object tmpResultRuntimeObject = null;
            bool requirePermission = true;
            bool isResultTrustedWithoutAptca = true;

            // Throw errors from initial parse, if any.
            if (!getLkg) {
                ThrowIfInitErrors();
            }

            //
            // check for a cached result
            //
            bool hasResult = false;
            SectionRecord sectionRecord = GetSectionRecord(configKey, getLkg);
            if (sectionRecord != null && sectionRecord.HasResult) {
                // Results should never be stored if the section has errors.
                Debug.Assert(!sectionRecord.HasErrors, "!sectionRecord.HasErrors");

                // Create the runtime object if requested and does not yet exist.
                if (getRuntimeObject && !sectionRecord.HasResultRuntimeObject) {
                    try {
                        sectionRecord.ResultRuntimeObject = GetRuntimeObject(sectionRecord.Result);
                    }
                    catch {
                        //
                        // Ignore the error if we are attempting to retreive
                        // the last known good configuration.
                        //
                        if (!getLkg) {
                            throw;
                        }
                    }
                }

                // Get the cached result.
                if (!getRuntimeObject || sectionRecord.HasResultRuntimeObject) {
                    requirePermission = sectionRecord.RequirePermission;
                    isResultTrustedWithoutAptca = sectionRecord.IsResultTrustedWithoutAptca;
                    tmpResult = sectionRecord.Result;
                    if (getRuntimeObject) {
                        tmpResultRuntimeObject = sectionRecord.ResultRuntimeObject;
                    }

                    hasResult = true;
                }
            }

            //
            // If there is no cached result, get the parent's section,
            // then merge it with our own input if we have any.
            //
            if (!hasResult) {
                FactoryRecord factoryRecord = null;
                bool hasInput = (sectionRecord != null && sectionRecord.HasInput);

                //
                // We want to cache results in a section record if:
                // - The request is made at this level, and so is likely to be
                //   made here again.
                // OR
                // - The section has input, in which case we want to 
                //   avoid evaluating the same input multiple times.
                //
                bool cacheResults = (requestIsHere || hasInput);

                bool isRootDeclaration;
                try {
                    //
                    // We need to get a factory record to:
                    // - Check whether the caller has permission to access a section.
                    // - Determine if this is the root declaration of a config section,
                    //   and thus the termination point for recursion.
                    // - Get a factory that can create a configuration section.
                    //
                    // Since most factories will be declared in machine.config and not in
                    // child config files, we do not optimize for checking whether a
                    // factory record is the root declaration, as the calculation at
                    // machine.config is trivial.
                    //
                    // It WILL be common in web scenarios for there to be 
                    // deep hierarchies of config files, most of which have
                    // sparse input. Therefore we do not want to retreive a 
                    // factory record if it is not necessary to do so, as 
                    // it would always lead to an order N-squared operation, 
                    // where N is the depth of the config hierarchy.
                    //
                    // We can skip the reteival of a factory record if:
                    // - This is the recursive call to GetSectionRecursive,
                    // AND
                    // - There is no section input at this level,
                    // AND
                    // - No factory is declared at this level.
                    //
                    // In this case, we'll simply continue the recursion to our parent.
                    //
                    if (requestIsHere) {
                        //
                        // Ensure that we have a valid factory record and a valid factory
                        // for creating sections when a request for a section is first
                        // made.
                        //
                        factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);

                        //
                        // If initialization is delayed, complete initialization if:
                        //  - We can't find the requested factory, and it therefore 
                        //    may be in the file we haven't yet read,
                        //  OR
                        //  - The definition of that factory is allowed at 
                        //    levels of the config hierarchy that have not
                        //    been initialized.
                        //
                        // This works for client config scenarios because the default
                        // for AllowExeDefinition is MachineToApplication. It would not
                        // be useful for Web scenarios, as most sections can be requested
                        // Everywhere.
                        //
                        // Note that configuration errors that may be present in the
                        // file where intialization is delayed will be ignored, and
                        // thus the order in which configuration sections are requested
                        // will affect results. This is considered OK as it is very
                        // expensive to determine configuration paths to 
                        // client user configuration files, which aren't needed by
                        // most applications.
                        //
                        if (   IsInitDelayed
                            && (   factoryRecord == null
                                || _initDelayedRoot.IsDefinitionAllowed(factoryRecord.AllowDefinition, factoryRecord.AllowExeDefinition))) {

                            //
                            // We are going to remove this record, so get any data we need
                            // before the reference to 'this' becomes invalid.
                            //
                            string configPath = this._configPath;
                            InternalConfigRoot configRoot = this._configRoot;

                            // Tell the host to no longer permit delayed initialization.
                            Host.RequireCompleteInit(_initDelayedRoot);

                            // Removed config at the root of where initialization is delayed.
                            _initDelayedRoot.Remove();

                            // Get the config record for this config path
                            BaseConfigurationRecord newRecord = (BaseConfigurationRecord) configRoot.GetConfigRecord(configPath);

                            // Repeat the call to GetSectionRecursive
                            newRecord.GetSectionRecursive(
                                configKey, getLkg, checkPermission, 
                                getRuntimeObject, requestIsHere,
                                out result, out resultRuntimeObject);

                            // Return and make no more references to this record.
                            return;
                        }

                        //
                        // For compatibility with previous versions, 
                        // return null if the section is not found
                        // or is a group.
                        //
                        if (factoryRecord == null || factoryRecord.IsGroup) {
                            return;
                        }

                        //
                        // Use the factory record's copy of the configKey,
                        // so that we don't store more than one instance
                        // of the same configKey.
                        //
                        configKey = factoryRecord.ConfigKey;
                    }
                    else if (hasInput) {
                        //
                        // We'll need a factory to evaluate the input.
                        //
                        factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
                        Debug.Assert(factoryRecord != null, "factoryRecord != null");
                    }
                    else {
                        //
                        // We don't need a factory record unless this is the root declaration.
                        // We know it is not the root declaration if there is no factory 
                        // declared here. This is important to avoid a walk up the config
                        // hierachy when there is no input in this record.
                        //
                        factoryRecord = GetFactoryRecord(configKey, false);
                        if (factoryRecord == null) {
                            isRootDeclaration = false;
                        }
                        else {
                            factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
                            Debug.Assert(factoryRecord != null, "factoryRecord != null");
                        }
                    }

                    // We need a factory record to check permission.
                    Debug.Assert(!checkPermission || factoryRecord != null, "!checkPermission || factoryRecord != null");

                    //
                    // If this is the root declaration, then we always want to cache
                    // the result, in order to prevent the section default from being 
                    // created multiple times.
                    //
                    if (isRootDeclaration) {
                        cacheResults = true;
                    }

                    //
                    // We'll need a section record to cache results,
                    // and maybe to use in creating the section default.
                    //
                    if (sectionRecord == null && cacheResults) {
                        sectionRecord = EnsureSectionRecord(configKey, true);
                    }

                    //
                    // Retrieve the parent's runtime object if the runtimeObject
                    // is requested, and we are not going to merge that input
                    // with input in this section.
                    //
                    bool getParentRuntimeObject = (getRuntimeObject && !hasInput);

                    object parentResult = null;
                    object parentResultRuntimeObject = null;
                    if (isRootDeclaration) {
                        //
                        // Create the default section.
                        //
                        // Use the existing section record to create it if there is no input,
                        // so that the cached result is attached to the correct record.
                        //
                        SectionRecord sectionRecordForDefault = (hasInput) ? null : sectionRecord;
                        CreateSectionDefault(configKey, getParentRuntimeObject, factoryRecord, sectionRecordForDefault, 
                                out parentResult, out parentResultRuntimeObject);
                    }
                    else {
                        //
                        // Get the parent section.
                        //
                        _parent.GetSectionRecursive(
                                configKey, false /* getLkg */, false /* checkPermission */, 
                                getParentRuntimeObject, false /* requestIsHere */,
                                out parentResult, out parentResultRuntimeObject);
                    }

                    if (hasInput) {
                        // 
                        // Evaluate the input.
                        //
                        // If Evaluate() encounters an error, it may not throw an exception
                        // when getLkg == true.
                        //
                        // The complete success of the evaluation is determined by the return value.
                        //
                        bool success = Evaluate(factoryRecord, sectionRecord, parentResult, getLkg, getRuntimeObject, 
                                out tmpResult, out tmpResultRuntimeObject);

                        Debug.Assert(success || getLkg, "success || getLkg");

                        if (!success) {
                            Debug.Assert(getLkg == true, "getLkg == true");
                            // Do not cache partial results if getLkg was specified.
                            cacheResults = false;
                        }
                    }
                    else {
                        //
                        // If we are going to cache results here, we will need
                        // to create a copy in the case of MgmtConfigurationRecord - 
                        // otherwise we could inadvertently return the parent to the user,
                        // which could then be modified.
                        //
                        if (sectionRecord != null) {
                            tmpResult = UseParentResult(configKey, parentResult, sectionRecord);
                            if (getRuntimeObject) {
                                //
                                // If the parent result is the same as the parent runtime object,
                                // then use the same copy of the parent result for our own runtime object.
                                //
                                if (object.ReferenceEquals(parentResult, parentResultRuntimeObject)) {
                                    tmpResultRuntimeObject = tmpResult;
                                }
                                else {
                                    tmpResultRuntimeObject = UseParentResult(configKey, parentResultRuntimeObject, sectionRecord);
                                }
                            }
                        }
                        else {
                            Debug.Assert(!requestIsHere, "!requestIsHere");

                            //
                            // We don't need to make a copy if we are not storing
                            // the result, and thus not returning the result to the 
                            // caller of GetSection.
                            //
                            tmpResult = parentResult;
                            tmpResultRuntimeObject = parentResultRuntimeObject;
                        }
                    }

                    //
                    // Determine which permissions are required of the caller.
                    //
                    if (cacheResults || checkPermission) {
                        requirePermission = factoryRecord.RequirePermission;
                        isResultTrustedWithoutAptca = factoryRecord.IsFactoryTrustedWithoutAptca;

                        //
                        // Cache the results.
                        //
                        if (cacheResults) {
                            if (sectionRecord == null) {
                                sectionRecord = EnsureSectionRecord(configKey, true);
                            }

                            sectionRecord.Result = tmpResult;
                            if (getRuntimeObject) {
                                sectionRecord.ResultRuntimeObject = tmpResultRuntimeObject;
                            }

                            sectionRecord.RequirePermission = requirePermission;
                            sectionRecord.IsResultTrustedWithoutAptca = isResultTrustedWithoutAptca;
                        }
                    }

                    hasResult = true;
                }
                catch {
                    //
                    // Ignore the error if we are attempting to retreive
                    // the last known good configuration.
                    //
                    if (!getLkg) {
                        throw;
                    }
                }

                //
                // If we don't have a result, ask our parent for its
                // last known good result.
                //
                if (!hasResult) {
                    Debug.Assert(getLkg == true, "getLkg == true");

                    _parent.GetSectionRecursive(
                        configKey, true /* getLkg */, checkPermission, 
                        true /* getRuntimeObject */, true /* requestIsHere */,
                        out result, out resultRuntimeObject);

                    return;
                }
            }

            //
            // Check if permission to access the section is allowed.
            //
            if (checkPermission) {
                CheckPermissionAllowed(configKey, requirePermission, isResultTrustedWithoutAptca);
            }

            //
            // Return the results.
            //
            result = tmpResult;
            if (getRuntimeObject) {
                resultRuntimeObject = tmpResultRuntimeObject;
            }
        }

        protected void CreateSectionDefault(
                string configKey, bool getRuntimeObject, FactoryRecord factoryRecord, SectionRecord sectionRecord,
                out object result, out object resultRuntimeObject) {

            result = null;
            resultRuntimeObject = null;

            SectionRecord sectionRecordForDefault;
            if (sectionRecord != null) {
                sectionRecordForDefault = sectionRecord;
            }
            else {
                sectionRecordForDefault = new SectionRecord(configKey);
            }

            object tmpResult = CallCreateSection(true, factoryRecord, sectionRecordForDefault, null, null, null, -1);
            object tmpResultRuntimeObject;
            if (getRuntimeObject) {
                tmpResultRuntimeObject = GetRuntimeObject(tmpResult);
            }
            else {
                tmpResultRuntimeObject = null;
            }

            result = tmpResult;
            resultRuntimeObject = tmpResultRuntimeObject;
        }


        //
        // Check if an input should be skipped based on inheritInChildApplications setting.
        //
        // If skipInChildApps == true (it means inheritInChildApplications=="false" in the location tag):
        //
        // - If _flags[IsAboveApplication]==true, that means the app pointed to by _configPath is above of the 
        //   current running app.  In another word, the running app is a child app of the app pointed to by _configPath.
        //   In this case, we should skip the input.
        //
        // - If _flags[IsAboveApplication]==false, that means the app pointed to by _configPath == current running app.
        //   In this case it's okay to use the input.
        //
        private bool ShouldSkipDueToInheritInChildApplications(bool skipInChildApps) {
            return (skipInChildApps && _flags[IsAboveApplication]);
        }

        // 
        // Evaluate the input.
        //
        // If Evaluate() encounters an error, it may not throw an exception
        // when getLkg == true.
        //
        // The complete success of the evaluation is determined by the return value.
        //
        private bool Evaluate(
                FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentResult, 
                bool getLkg, bool getRuntimeObject, out object result, out object resultRuntimeObject) {

            result = null;
            resultRuntimeObject = null;

            //
            // Store results in temporary variables, because we don't want to return
            // results if an exception is thrown.
            //
            object tmpResult = null;
            object tmpResultRuntimeObject = null;

            // Factory record should be error-free.
            Debug.Assert(!factoryRecord.HasErrors, "!factoryRecord.HasErrors");

            // We should have some input
            Debug.Assert(sectionRecord.HasInput, "sectionRecord.HasInput");

            //
            // Grab inputs before checking result, 
            // as inputs may be cleared once the result is set.
            //
            List<SectionInput> locationInputs = sectionRecord.LocationInputs;
            SectionInput fileInput = sectionRecord.FileInput;

            //
            // Now that we have our inputs, lets check if there
            // is a result, because if there is, our inputs might
            // have been invalidated.
            //
            bool success = false;
            if (sectionRecord.HasResult) {
                // Results should never be stored if the section has errors.
                Debug.Assert(!sectionRecord.HasErrors, "!sectionRecord.HasErrors");

                // Create the runtime object if requested and it does not yet exist.
                if (getRuntimeObject && !sectionRecord.HasResultRuntimeObject) {
                    try {
                        sectionRecord.ResultRuntimeObject = GetRuntimeObject(sectionRecord.Result);
                    }
                    catch {
                        //
                        // Ignore the error if we are attempting to retreive
                        // the last known good configuration.
                        //
                        if (!getLkg) {
                            throw;
                        }
                    }
                }

                // Get the cached result.
                if (!getRuntimeObject || sectionRecord.HasResultRuntimeObject) {
                    tmpResult = sectionRecord.Result;
                    if (getRuntimeObject) {
                        tmpResultRuntimeObject = sectionRecord.ResultRuntimeObject;
                    }

                    success = true;
                }
            }

            if (!success) {
                Exception savedException = null;
                try {
                    string configKey = factoryRecord.ConfigKey;
                    string [] keys = configKey.Split(ConfigPathSeparatorParams);
                    object currentResult = parentResult;

                    //
                    // Evaluate location inputs
                    //
                    if (locationInputs != null) {
                        foreach (SectionInput locationInput in locationInputs) {
                            if (!locationInput.HasResult) {
                                locationInput.ThrowOnErrors();
                                bool isTrusted = Host.IsTrustedConfigPath(locationInput.SectionXmlInfo.DefinitionConfigPath);
                                locationInput.Result = EvaluateOne(keys, locationInput, isTrusted, factoryRecord, sectionRecord, currentResult);
                            }

                            currentResult = locationInput.Result;
                        }
                    }

                    //
                    // Evaluate file input
                    //
                    if (fileInput != null) {
                        if (!fileInput.HasResult) {
                            fileInput.ThrowOnErrors();
                            bool isTrusted = _flags[IsTrusted];
                            fileInput.Result = EvaluateOne(keys, fileInput, isTrusted, factoryRecord, sectionRecord, currentResult);
                        }

                        currentResult = fileInput.Result;
                    }
                    else {
                        //
                        // The section needs its own copy of the result that is distinct
                        // from its location parent result.
                        //
                        Debug.Assert(locationInputs != null, "locationInputs != null");
                        currentResult = UseParentResult(configKey, currentResult, sectionRecord);
                    }

                    if (getRuntimeObject) {
                        tmpResultRuntimeObject = GetRuntimeObject(currentResult);
                    }

                    tmpResult = currentResult;
                    success = true;
                }
                catch (Exception e) {
                    //
                    // Catch the exception if LKG is requested and we have
                    // location input to fall back on.
                    //
                    if (getLkg && locationInputs != null) {
                        savedException = e;
                    }
                    else {
                        throw;
                    }

                }

                //
                // If getLkg, then return a result from the last valid location input.
                //
                if (!success) {
                    Debug.Assert(getLkg == true, "getLkg == true");

                    int i = locationInputs.Count;
                    while (--i >= 0) {
                        SectionInput locationInput = locationInputs[i];
                        if (locationInput.HasResult) {
                            if (getRuntimeObject && !locationInput.HasResultRuntimeObject) {
                                try {
                                    locationInput.ResultRuntimeObject = GetRuntimeObject(locationInput.Result);
                                }
                                catch {
                                }
                            }

                            if (!getRuntimeObject || locationInput.HasResultRuntimeObject) {
                                tmpResult = locationInput.Result;
                                if (getRuntimeObject) {
                                    tmpResultRuntimeObject = locationInput.ResultRuntimeObject;
                                }

                                break;
                            }
                        }
                    }

                    if (i < 0) {
                        throw savedException;
                    }
                }
            }

            //
            // If evaluation was successful, we can remove any saved rawXml.
            //
            if (success && !_flags[SupportsKeepInputs]) {
                sectionRecord.ClearRawXml();
            }

            result = tmpResult;
            if (getRuntimeObject) {
                resultRuntimeObject = tmpResultRuntimeObject;
            }

            return success;
        }

        private object EvaluateOne(
                string[] keys, SectionInput input, bool isTrusted, 
                FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentResult) {

            object result;
            try {
                ConfigXmlReader reader = GetSectionXmlReader(keys, input);
                if (reader == null) {
                    //
                    // If section is not found in a file, use the parent result
                    //
                    result = UseParentResult(factoryRecord.ConfigKey, parentResult, sectionRecord);
                }
                else {
                    result = CallCreateSection(
                            isTrusted, factoryRecord, sectionRecord, parentResult, 
                            reader, input.SectionXmlInfo.Filename, input.SectionXmlInfo.LineNumber);
                }
            }
            catch (Exception e) {
                throw ExceptionUtil.WrapAsConfigException(
                        SR.GetString(SR.Config_exception_creating_section, factoryRecord.ConfigKey), 
                        e, input.SectionXmlInfo);
            } 
            catch {
                throw ExceptionUtil.WrapAsConfigException(
                        SR.GetString(SR.Config_exception_creating_section, factoryRecord.ConfigKey), 
                        null, input.SectionXmlInfo);
            }

            return result;
        }

        //
        // Create a single cached instance of UnrestrictedConfigPermission.
        //
        private static ConfigurationPermission UnrestrictedConfigPermission {
            get {
                if (s_unrestrictedConfigPermission == null) {
                    s_unrestrictedConfigPermission = new ConfigurationPermission(PermissionState.Unrestricted);
                }

                return s_unrestrictedConfigPermission;
            }
        }

        //
        // Check whether permission to the section is allowed to the caller.
        //
        private void CheckPermissionAllowed(string configKey, bool requirePermission, bool isTrustedWithoutAptca) {
            //
            // Demand unrestricted ConfigurationPermission if the section requires it
            //
            if (requirePermission) {

                try {
                    UnrestrictedConfigPermission.Demand();
                }
                catch (SecurityException e) {
                    //
                    // Add a nice error message that includes the sectionName and explains
                    // how to use the requirePermission attribute.
                    //
                    throw new SecurityException(
                            SR.GetString(SR.ConfigurationPermission_Denied, configKey), 
                            e);
                }
            }

            //
            // Ensure that the recepient isn't receiving an object they otherwise
            // wouldn't be able to create due to Aptca.
            //
            if (isTrustedWithoutAptca && !Host.IsFullTrustSectionWithoutAptcaAllowed(this)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Section_from_untrusted_assembly, configKey));
            }
        }


        private ConfigXmlReader FindSection(string [] keys, SectionXmlInfo sectionXmlInfo, out int lineNumber) {
            lineNumber = 0;
            ConfigXmlReader section = null;
            try {
                using (Impersonate()) {
                    using (Stream stream = Host.OpenStreamForRead(sectionXmlInfo.Filename)) {
                        if (       !_flags[SupportsRefresh] 
                                && (stream == null || HasStreamChanged(sectionXmlInfo.Filename, sectionXmlInfo.StreamVersion))) {

                            throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), sectionXmlInfo.Filename, 0);
                        }

                        if (stream != null) {
                            using (XmlUtil xmlUtil = new XmlUtil(stream, sectionXmlInfo.Filename, true)) {
                                if (sectionXmlInfo.SubPath == null) {
                                    section = FindSectionRecursive(keys, 0, xmlUtil, ref lineNumber);
                                }
                                else {
                                    // search children of <configuration> for <location>
                                    xmlUtil.ReadToNextElement();
                                    while (xmlUtil.Reader.Depth > 0) {
                                        if (xmlUtil.Reader.Name == KEYWORD_LOCATION) {
                                            bool   locationValid = false;
                                            string locationSubPathAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_LOCATION_PATH);

                                            try {
                                                locationSubPathAttribute = NormalizeLocationSubPath(locationSubPathAttribute, xmlUtil);
                                                locationValid = true;
                                            }
                                            catch (ConfigurationException ce) {
                                                xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.NonSpecific);
                                            }

                                            if (locationValid &&
                                                 StringUtil.EqualsIgnoreCase(sectionXmlInfo.SubPath, locationSubPathAttribute)) {

                                                section = FindSectionRecursive(keys, 0, xmlUtil, ref lineNumber);
                                                if (section != null)
                                                    break;
                                            }
                                        }

                                        xmlUtil.SkipToNextElement();
                                    }
                                }

                                // Throw accumulated errors
                                ThrowIfParseErrors(xmlUtil.SchemaErrors);
                            }
                        }
                    }
                }
            }
            // Don't allow frames up the stack to run exception filters while impersonated.
            catch {
                throw;
            }


            return section;
        }

        private ConfigXmlReader FindSectionRecursive(string [] keys, int iKey, XmlUtil xmlUtil, ref int lineNumber) {
            string name = keys[iKey];
            ConfigXmlReader section = null;

            int depth = xmlUtil.Reader.Depth;
            xmlUtil.ReadToNextElement();

            while (xmlUtil.Reader.Depth > depth) {
                if (xmlUtil.Reader.Name == name) {
                    if (iKey < keys.Length - 1) {
                        //
                        // We haven't reached the section yet, so keep evaluating
                        //
                        section = FindSectionRecursive(keys, iKey + 1, xmlUtil, ref lineNumber);
                        if (section != null) {
                            break;
                        }

                        continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
                    }
                    else {
                        //
                        // We've reached the section. Load the section into a string.
                        //
                        string filename = ((IConfigErrorInfo)xmlUtil).Filename;
                        int lineOffset = xmlUtil.Reader.LineNumber;
                        string rawXml = xmlUtil.CopySection();
                        section = new ConfigXmlReader(rawXml, filename, lineOffset);
                        break;
                    }
                }
                else if (iKey == 0 && xmlUtil.Reader.Name == KEYWORD_LOCATION) {
                    string locationSubPath = xmlUtil.Reader.GetAttribute(KEYWORD_LOCATION_PATH);
                    bool isValid = false;
                    try {
                        locationSubPath = NormalizeLocationSubPath(locationSubPath, xmlUtil);
                        isValid = true;
                    }
                    catch (ConfigurationException ce) {
                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.NonSpecific);
                    }

                    if (isValid && locationSubPath == null) {
                        //
                        // Location sections that don't have a subpath are treated
                        // as ordinary sections.
                        //
                        section = FindSectionRecursive(keys, iKey, xmlUtil, ref lineNumber);
                        if (section != null) {
                            break;
                        }

                        continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
                    }
                }

                xmlUtil.SkipToNextElement();
            }

            return section;
        }

        private ConfigXmlReader LoadConfigSource(string name, SectionXmlInfo sectionXmlInfo) {
            string configSourceStreamName = sectionXmlInfo.ConfigSourceStreamName;
            
            try {
                using (Impersonate()) {
                    using (Stream stream = Host.OpenStreamForRead(configSourceStreamName)) {
                        if (stream == null) {
                            throw new ConfigurationErrorsException(
                                    SR.GetString(SR.Config_cannot_open_config_source, sectionXmlInfo.ConfigSource),
                                    sectionXmlInfo);
                        }

                        using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, true)) {
                            if (xmlUtil.Reader.Name != name) {
                                throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_file_format), xmlUtil);
                            }

                            // Check for protectionProvider
                            string protectionProviderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_PROTECTION_PROVIDER);
                            if (protectionProviderAttribute != null) {
                                if (xmlUtil.Reader.AttributeCount != 1) {
                                    // Error: elements with protectionProvider should not have other attributes
                                    throw new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_syntax_error), xmlUtil);
                                }
        
                                sectionXmlInfo.ProtectionProviderName = ValidateProtectionProviderAttribute(protectionProviderAttribute, xmlUtil);
                            }
                            
                            int lineOffset = xmlUtil.Reader.LineNumber;
                            string rawXml = xmlUtil.CopySection();

                            // Detect if there is any XML left over after the section
                            while (!xmlUtil.Reader.EOF) {
                                XmlNodeType t = xmlUtil.Reader.NodeType;
                                if (t != XmlNodeType.Comment) {
                                    throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_file_format), xmlUtil);
                                }

                                xmlUtil.Reader.Read();
                            }

                            ConfigXmlReader section = new ConfigXmlReader(rawXml, configSourceStreamName, lineOffset);
                            return section;
                        }
                    }
                }
            }
            catch {
                // Don't allow frames up the stack to run exception filters while impersonated.
                throw;
            }
        }

        protected ConfigXmlReader GetSectionXmlReader(string[] keys, SectionInput input) {
            ConfigXmlReader reader = null;
            string filename = input.SectionXmlInfo.Filename;
            int lineNumber = input.SectionXmlInfo.LineNumber;

            try {
                string name = keys[keys.Length-1];
                string rawXml = input.SectionXmlInfo.RawXml;
                if (rawXml != null) {
                    // Use the stored raw xml to provide the content of the section.
                    reader = new ConfigXmlReader(rawXml, input.SectionXmlInfo.Filename, input.SectionXmlInfo.LineNumber);
                }
                else if (!String.IsNullOrEmpty(input.SectionXmlInfo.ConfigSource)) {
                    // Load the  config source to provide the content of the section.
                    filename = input.SectionXmlInfo.ConfigSourceStreamName;
                    lineNumber = 0;
                    reader = LoadConfigSource(name, input.SectionXmlInfo);
                }
                else {
                    // Find the content of the section in the config file.
                    lineNumber = 0;
                    reader = FindSection(keys, input.SectionXmlInfo, out lineNumber);
                }

                if (reader != null) {
                    if (!input.IsProtectionProviderDetermined) {
                        input.ProtectionProvider = GetProtectionProviderFromName(input.SectionXmlInfo.ProtectionProviderName, false);
                    }

                    if (input.ProtectionProvider != null) {
                        reader = DecryptConfigSection(reader, input.ProtectionProvider);
                    }
                }
            }

            //
            // Guarantee that exceptions contain the name of the stream and an approximate line number.
            //
            catch (Exception e) {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, filename, lineNumber);
            } 
            catch {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), null, filename, lineNumber);
            }

            return reader;
        }

        internal string DefaultProviderName {
            get { 
                return ProtectedConfig.DefaultProvider;
            }
        }
        
        internal ProtectedConfigurationProvider GetProtectionProviderFromName(string providerName, bool throwIfNotFound) {
            ProtectedConfigurationProvider provider = null;

            if (String.IsNullOrEmpty(providerName)) {
                if (throwIfNotFound) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.ProtectedConfigurationProvider_not_found, providerName));
                }
                else {
                    return null;
                }
            }
            
            provider = ProtectedConfig.GetProviderFromName(providerName);

            return provider;
        }

        private ProtectedConfigurationSection ProtectedConfig {
            get {
                if (!_flags[ProtectedDataInitialized]) {
                    InitProtectedConfigurationSection();
                }

                return _protectedConfig;
            }
        }

        internal void InitProtectedConfigurationSection() {
            if (!_flags[ProtectedDataInitialized]) {
                _protectedConfig = GetSection(BaseConfigurationRecord.RESERVED_SECTION_PROTECTED_CONFIGURATION, false, false) as ProtectedConfigurationSection;

                Debug.Assert(_protectedConfig != null, "<configProtectedData> section should always be available because it's a built-in section");

                _flags[ProtectedDataInitialized] = true;
            }
        }

        protected object CallCreateSection(bool inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentConfig, ConfigXmlReader reader, string filename, int line) {
            object config;

            // Call into config section while impersonating process or UNC identity
            // so that the section could read files from disk if needed
            try {
                using (Impersonate()) {
                    config = CreateSection(inputIsTrusted, factoryRecord, sectionRecord, parentConfig, reader);
                    if (config == null && parentConfig != null) {
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_object_is_null), filename, line);
                    }
                }
            }
            catch (Exception e) {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), e, filename, line);
            }
            catch {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), null, filename, line);
            }

            return config;
        }

        //
        //
        //
        internal bool IsRootDeclaration(string configKey, bool implicitIsRooted) {
            if (!implicitIsRooted && IsImplicitSection(configKey)) {
                return false;
            }

            return (_parent.IsRootConfig || _parent.FindFactoryRecord(configKey, true) == null);
        }
        
        // Search the config hierarchy for a FactoryRecord.
        // Note that callers should check whether the returned factory has errors.
        internal FactoryRecord FindFactoryRecord(string configKey, bool permitErrors, out BaseConfigurationRecord configRecord) {
            configRecord = null;
            BaseConfigurationRecord tConfigRecord = this;

            while (!tConfigRecord.IsRootConfig) {
                FactoryRecord factoryRecord = tConfigRecord.GetFactoryRecord(configKey, permitErrors);
                if (factoryRecord != null) {
#if DBG                    
                    if (IsImplicitSection(configKey) && !factoryRecord.HasErrors) {
                        Debug.Assert(tConfigRecord._parent.IsRootConfig, 
                            "Implicit section should be found only at the record beneath the root (e.g. machine.config)");
                    }
#endif                    

                    configRecord = tConfigRecord;
                    return factoryRecord;
                }

                tConfigRecord = tConfigRecord._parent;
            }

            return null;
        }


        internal FactoryRecord FindFactoryRecord(string configKey, bool permitErrors) {
            BaseConfigurationRecord dummy;
            return FindFactoryRecord(configKey, permitErrors, out dummy);
        }

        //
        // FindAndEnsureFactoryRecord:
        //  
        // - Find the nearest factory record
        // - Determine if it is the root
        // - Create the factory in the root if it doesn't exist.
        // - Determine if the factory type is from a global assembly without APTCA
        // - Copy the factory and IsFactoryTrustedWithoutAptca bit into the child record
        //
        private FactoryRecord FindAndEnsureFactoryRecord(string configKey, out bool isRootDeclaredHere) {
            isRootDeclaredHere = false;

            BaseConfigurationRecord configRecord;
            FactoryRecord factoryRecord = FindFactoryRecord(configKey, false, out configRecord);
            if (factoryRecord != null && !factoryRecord.IsGroup) {
                //
                // Find the root declaration
                //
                FactoryRecord rootFactoryRecord = factoryRecord;
                BaseConfigurationRecord rootConfigRecord = configRecord;

                BaseConfigurationRecord currentConfigRecord = configRecord._parent;
                while (!currentConfigRecord.IsRootConfig) {
                    BaseConfigurationRecord tempConfigRecord;
                    FactoryRecord tempFactoryRecord = currentConfigRecord.FindFactoryRecord(configKey, false, out tempConfigRecord);
                    if (tempFactoryRecord == null)
                        break;

                    rootFactoryRecord = tempFactoryRecord;
                    rootConfigRecord = tempConfigRecord;

                    // continue the search from the parent of the configRecord we found
                    currentConfigRecord = tempConfigRecord.Parent;
                }

                //
                // A child factory record must be equivalent to its parent,
                // so if the child has no errors, the parent must also have no errors.
                //
                Debug.Assert(!rootFactoryRecord.HasErrors, "!rootFactoryRecord.HasErrors");
                if (rootFactoryRecord.Factory == null) {
                    try {
                        //
                        // Create the factory from the type string, and cache it
                        //
                        object factory = rootConfigRecord.CreateSectionFactory(rootFactoryRecord);
                        bool isFactoryTrustedWithoutAptca = TypeUtil.IsTypeFromTrustedAssemblyWithoutAptca(factory.GetType());
                        rootFactoryRecord.Factory = factory;
                        rootFactoryRecord.IsFactoryTrustedWithoutAptca = isFactoryTrustedWithoutAptca;
                    }
                    catch (Exception e) {
                        throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), e, factoryRecord);
                    }
                    catch {
                        throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), null, factoryRecord);
                    }
                }

                if (factoryRecord.Factory == null) {
                    factoryRecord.Factory = rootFactoryRecord.Factory;
                    factoryRecord.IsFactoryTrustedWithoutAptca = rootFactoryRecord.IsFactoryTrustedWithoutAptca;
                }

                isRootDeclaredHere = Object.ReferenceEquals(this, rootConfigRecord);
            }

            return factoryRecord;
        }

        private Hashtable ScanFactories(XmlUtil xmlUtil) {
            Hashtable factoryList;

            factoryList = new Hashtable();
            
            if (xmlUtil.Reader.NodeType != XmlNodeType.Element || xmlUtil.Reader.Name != KEYWORD_CONFIGURATION) {
                string safeFilename = ConfigurationErrorsException.AlwaysSafeFilename(((IConfigErrorInfo)xmlUtil).Filename);

                throw new ConfigurationErrorsException(
                        SR.GetString(SR.Config_file_doesnt_have_root_configuration, safeFilename), 
                        xmlUtil);
            }

            // Ignore xmlns attribute
            while (xmlUtil.Reader.MoveToNextAttribute()) {
                switch (xmlUtil.Reader.Name) {
                case KEYWORD_XMLNS:
                    if (xmlUtil.Reader.Value == KEYWORD_CONFIGURATION_NAMESPACE) {
                        _flags[NamespacePresentInFile] = true;
                        _flags[NamespacePresentCurrent] = true;
                    } else {
                        ConfigurationErrorsException ce;

                        ce = new ConfigurationErrorsException(
                                SR.GetString(SR.Config_namespace_invalid, xmlUtil.Reader.Value, KEYWORD_CONFIGURATION_NAMESPACE),
                                xmlUtil);
                        
                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Global);
                    }
                    
                    break;

                default:
                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific);
                    break;
                }
            }

            // move to first child of <configuration>
            xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
            if (xmlUtil.Reader.Depth == 1 && xmlUtil.Reader.Name == KEYWORD_CONFIGSECTIONS) {
                xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific);
                ScanFactoriesRecursive(xmlUtil, string.Empty, factoryList);
            }

            return factoryList;
        }


        // Scans the <configSections> section of a configuration file.  The function is recursive
        // to traverse arbitrarily nested config groups.
        //
        //     <sectionGroup name="foo">
        //         <sectionGroup name="bar">
        //             <section name="fooBarSection" type="..." />
        //     ...
        //
        // Note: This function valiates that the factory record has not been
        //       declared before in a parent record. (it does not check
        //       current record, which allows you to update list)
        //
        private void ScanFactoriesRecursive(XmlUtil xmlUtil, string parentConfigKey, Hashtable factoryList) {

            // discard any accumulated local errors
            xmlUtil.SchemaErrors.ResetLocalErrors();

            int depth = xmlUtil.Reader.Depth;
            xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);

            while (xmlUtil.Reader.Depth == depth + 1) {
                bool    positionedAtNextElement = false;
                
                switch (xmlUtil.Reader.Name) {
                    //
                    // Handle <sectionGroup name="groupName" [type="typename"] />
                    //
                    case KEYWORD_SECTIONGROUP: {
                        string tagName = null;
                        string typeName = null;

                        int lineNumber = xmlUtil.Reader.LineNumber;
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            switch (xmlUtil.Reader.Name) {
                                case KEYWORD_SECTIONGROUP_NAME:
                                    tagName = xmlUtil.Reader.Value;
                                    VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
                                    break;

                                case KEYWORD_SECTIONGROUP_TYPE:
                                    xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
                                    break;

                                default:
                                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
                                    break;
                            }
                        }
                        xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element

                        if (!xmlUtil.VerifyRequiredAttribute(
                                tagName, 
                                KEYWORD_SECTIONGROUP_NAME,
                                ExceptionAction.NonSpecific)) {
                                        
                            //
                            // Without a name, we cannot continue parsing the sections and groups within.
                            // Skip the entire section.
                            //
                            xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
                            xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                        }
                        else {
                            string configKey = CombineConfigKey(parentConfigKey, tagName);

                            FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
                            if (factoryRecord != null) {
                                // Error: duplicate sectionGroup declaration
                                xmlUtil.SchemaErrors.AddError(
                                        new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
                                        ExceptionAction.Local);

                            } else {
                                FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
                                if (parentFactoryRecord != null) {
                                    configKey = parentFactoryRecord.ConfigKey;


                                    // make sure that an ancestor has not defined a section with the same name as the group
                                    if (    !parentFactoryRecord.IsGroup              ||
                                            !parentFactoryRecord.IsEquivalentSectionGroupFactory(Host, typeName)) {
                                         
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                }

                                if (parentFactoryRecord != null) {
                                    factoryRecord = parentFactoryRecord.CloneSectionGroup(typeName, xmlUtil.Filename, lineNumber);
                                }
                                else {
                                    factoryRecord = new FactoryRecord(configKey, parentConfigKey, tagName, typeName, xmlUtil.Filename, lineNumber);
                                }

                                factoryList[configKey] = factoryRecord;
                            }

                            // Add any errors we may have encountered
                            factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));

                            // continue recursive scan
                            ScanFactoriesRecursive(xmlUtil, configKey, factoryList);
                        }

                        continue;
                    }

                    case KEYWORD_SECTION: {
                        string tagName = null;
                        string typeName = null;
                        ConfigurationAllowDefinition    allowDefinition = ConfigurationAllowDefinition.Everywhere;
                        ConfigurationAllowExeDefinition allowExeDefinition = ConfigurationAllowExeDefinition.MachineToApplication;
                        bool allowLocation = true;
                        bool restartOnExternalChanges = true;
                        bool requirePermission = true;
                        bool gotType = false;

                        // parse section attributes
                        int lineNumber = xmlUtil.Reader.LineNumber;
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            switch (xmlUtil.Reader.Name) {
                                case KEYWORD_SECTION_NAME:
                                    tagName = xmlUtil.Reader.Value;
                                    VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
                                    break;

                                case KEYWORD_SECTION_TYPE:
                                    xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
                                    gotType = true;
                                    break;

                                case KEYWORD_SECTION_ALLOWLOCATION:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out allowLocation);
                                    break;

                                case KEYWORD_SECTION_ALLOWEXEDEFINITION:
                                    try {
                                        allowExeDefinition = AllowExeDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
                                    }
                                    catch (ConfigurationException ce) {
                                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                    }

                                    break;

                                case KEYWORD_SECTION_ALLOWDEFINITION:
                                    try {
                                        allowDefinition = AllowDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
                                    }
                                    catch (ConfigurationException ce) {
                                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                    }

                                    break;

                                case KEYWORD_SECTION_RESTARTONEXTERNALCHANGES:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out restartOnExternalChanges);

                                    break;

                                case KEYWORD_SECTION_REQUIREPERMISSION:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out requirePermission);

                                    break;

                                default:
                                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
                                    break;
                            }
                        }

                        xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element

                        if (!xmlUtil.VerifyRequiredAttribute(
                                tagName, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {

                            //
                            // Without a name, we cannot continue to create a factoryRecord.
                            //
                            xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
                        }
                        else {
                            // Verify that the Type attribute was present.
                            // Note that 'typeName' will be null if the attribute was present
                            // but specified as an empty string.
                            if (!gotType) {
                                xmlUtil.AddErrorRequiredAttribute(KEYWORD_SECTION_TYPE, ExceptionAction.Local);
                            }

                            string configKey = CombineConfigKey(parentConfigKey, tagName);

                            FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
                            if (factoryRecord != null) {
                                // Error: duplicate section declaration
                                xmlUtil.SchemaErrors.AddError(
                                        new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
                                        ExceptionAction.Local);
                            } else {
                                FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
                                if (parentFactoryRecord != null) {
                                    configKey = parentFactoryRecord.ConfigKey;

                                    // make sure that an ancestor has not defined a section with the same name as the group
                                    if (parentFactoryRecord.IsGroup) {
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                    else if (!parentFactoryRecord.IsEquivalentSectionFactory(Host, typeName, allowLocation, allowDefinition, allowExeDefinition, restartOnExternalChanges, requirePermission)) {
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                }

                                if (parentFactoryRecord != null) {
                                    // Note - Clone will propagate the IsFromTrustedConfigRecord bit, 
                                    // which is what we want - if this record is a duplicate of an ancestor,
                                    // the ancestor may be from a trusted config record.
                                    factoryRecord = parentFactoryRecord.CloneSection(xmlUtil.Filename, lineNumber);
                                }
                                else {
                                    factoryRecord = new FactoryRecord(
                                            configKey, 
                                            parentConfigKey, 
                                            tagName, 
                                            typeName, 
                                            allowLocation, 
                                            allowDefinition, 
                                            allowExeDefinition, 
                                            restartOnExternalChanges, 
                                            requirePermission,
                                            _flags[IsTrusted],
                                            false,  // isUndeclared
                                            xmlUtil.Filename,
                                            lineNumber);
                                }

                                factoryList[configKey] = factoryRecord;
                            }

                            // Add any errors we may have encountered
                            factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
                        }
                    }
                    break;

                    case KEYWORD_REMOVE: {
                        string name = null;
                        int lineNumber = -1;

                        // parse attributes
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            if (xmlUtil.Reader.Name != KEYWORD_SECTION_NAME) {
                                xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific);
                            }

                            name = xmlUtil.Reader.Value;
                            lineNumber = xmlUtil.Reader.LineNumber;
                        }
                        xmlUtil.Reader.MoveToElement();

                        if (xmlUtil.VerifyRequiredAttribute(
                                name, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {

                            VerifySectionName(name, xmlUtil, ExceptionAction.NonSpecific, false);
                        }
                    }
                    break;

                    case KEYWORD_CLEAR: {
                        xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific);
                    }
                    break;

                    default:
                        xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
                        xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                        positionedAtNextElement = true;
                        break;
                }

                if (!positionedAtNextElement) {
                    // Need to read to next element, and check if an unrecognized child
                    // element is found.
                    xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
                    
                    // unrecognized children are not allowed in <configSections>
                    if (xmlUtil.Reader.Depth > depth + 1) {
                        xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
                    
                        // Lets try to backup to where we are suppose to be
                        while (xmlUtil.Reader.Depth > (depth + 1)) {
                            xmlUtil.ReadToNextElement();
                        }
                    }
                }
            }
        }

        // ExeDefinitionToEnum
        //
        // Translate an ExeDefinition string from the Declaration in a file
        // to the appropriate enumeration
        //
        // Parameters:
        //   allowExeDefinition - string representation of value
        //   xmlUtil [optional] - can provide better error
        //
        static internal ConfigurationAllowExeDefinition 
        AllowExeDefinitionToEnum(string allowExeDefinition, XmlUtil xmlUtil)
        {
            switch (allowExeDefinition) 
            {
            case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY:
                return ConfigurationAllowExeDefinition.MachineOnly;
              
            case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION:
                return ConfigurationAllowExeDefinition.MachineToApplication;
              
            case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING:
                return ConfigurationAllowExeDefinition.MachineToRoamingUser;
              
            case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL:
                return ConfigurationAllowExeDefinition.MachineToLocalUser;
              
            default:
                throw new ConfigurationErrorsException(
                        SR.GetString(SR.Config_section_allow_exe_definition_attribute_invalid),
                        xmlUtil);
            }
        }
        
        static internal ConfigurationAllowDefinition
        AllowDefinitionToEnum(string allowDefinition, XmlUtil xmlUtil) {
            switch (xmlUtil.Reader.Value) {
            case KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE:
                return ConfigurationAllowDefinition.Everywhere;

            case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY:
                return ConfigurationAllowDefinition.MachineOnly;

            case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION:
                return ConfigurationAllowDefinition.MachineToApplication;

            case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT:
                return ConfigurationAllowDefinition.MachineToWebRoot;

            default:
                throw new ConfigurationErrorsException(
                        SR.GetString(SR.Config_section_allow_definition_attribute_invalid), 
                        xmlUtil);
            }
        }

        static internal string CombineConfigKey(string parentConfigKey, string tagName) {
            if (String.IsNullOrEmpty(parentConfigKey)) {
                return tagName;
            }

            if (String.IsNullOrEmpty(tagName)) {
                return parentConfigKey;
            }

            return parentConfigKey + "/" + tagName;
        }

        static internal void SplitConfigKey(string configKey, out string group, out string name) {
            int lastSlash = configKey.LastIndexOf('/');
            if (lastSlash == -1) {
                group = string.Empty;
                name = configKey;
            }
            else {
                group = configKey.Substring(0, lastSlash);
                name = configKey.Substring(lastSlash + 1);
            }
        }

        // Return true if a parent is locked or locks its children.
        private bool IsParentCausingLock(string configKey) {
            BaseConfigurationRecord parent = _parent;
            while (!parent.IsRootConfig) {
                SectionRecord sectionRecord = parent.GetSectionRecord(configKey, true);
                if (    sectionRecord != null && 
                        (sectionRecord.LockChildren || sectionRecord.Locked)) {

                    return true;
                }

                parent = parent._parent;
            }

            return false;
        }

        // Return true if a section is locked.
        protected bool IsSectionLocked(string configKey) {
            SectionRecord sectionRecord = GetSectionRecord(configKey, true);

            // If the section record exists, the locked bit will be set if it is locked.
            if (sectionRecord != null) {
                if (sectionRecord.Locked) {
                    return true;
                }

                // Because we are so close to RTM, we cannot change the code to avoid
                // the call below to IsParentCausingLock(). However, we can still verify
                // that the parent isn't causing a lock.
                Debug.Assert(!IsParentCausingLock(configKey), "!IsParentCausingLock(configKey)");
            }

            // Check if parent causes a lock.
            return IsParentCausingLock(configKey);
        }

        // VerifySectionUnlocked
        //
        // Verify the section we have defined is unlocked
        //
        protected void VerifySectionUnlocked(string configKey, IConfigErrorInfo configErrorInfo) {
            if (IsSectionLocked(configKey)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_section_locked), configErrorInfo);
            }
        }

        private void ScanSections(XmlUtil xmlUtil) {
            ScanSectionsRecursive(xmlUtil, string.Empty, false, null, false, false);
        }

        private void ScanSectionsRecursive(
                XmlUtil xmlUtil, string parentConfigKey, bool inLocation, string locationSubPath, 
                bool lockChildren, bool skipInChildApps) {

            // discard any accumulated local errors
            xmlUtil.SchemaErrors.ResetLocalErrors();

            int depth;

            // only move to child nodes when not on first level (we've already passed the first <configsections>)
            if (parentConfigKey.Length == 0 && !inLocation) {
                depth = 0;
            }
            else {
                depth = xmlUtil.Reader.Depth;
                xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
            }

            while (xmlUtil.Reader.Depth == depth + 1) {

                string tagName = xmlUtil.Reader.Name;

                //
                // Check for reserved elements before looking up the factory,
                // which may have the same name if it is in error.
                //
                if (tagName == KEYWORD_CONFIGSECTIONS) {
                    // Error: duplicate <configSections> tag, or <configSections> not the first tag under <configuration>
                    xmlUtil.SchemaErrors.AddError(
                            new ConfigurationErrorsException(SR.GetString(SR.Config_client_config_too_many_configsections_elements, tagName), xmlUtil),
                            ExceptionAction.NonSpecific);

                    xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                    continue;
                }

                if (tagName == KEYWORD_LOCATION) {
                    if (parentConfigKey.Length > 0 || inLocation) {
                        // Error: <location> section not at top level
                        xmlUtil.SchemaErrors.AddError(
                                new ConfigurationErrorsException(SR.GetString(SR.Config_location_location_not_allowed), xmlUtil),
                                ExceptionAction.Global);

                        xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                    }
                    else {
                        // Recurse into the location section
                        ScanLocationSection(xmlUtil);
                    }

                    continue;
                }

                string configKey = CombineConfigKey(parentConfigKey, tagName);
                FactoryRecord factoryRecord = FindFactoryRecord(configKey, true);


                if (factoryRecord == null) {
                    // 
                    //
                    if (!ClassFlags[ClassIgnoreLocalErrors]) {
                        xmlUtil.SchemaErrors.AddError(
                                new ConfigurationErrorsException(SR.GetString(SR.Config_unrecognized_configuration_section, configKey), xmlUtil),
                                ExceptionAction.Local);
                    }

                    VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);

                    factoryRecord = new FactoryRecord(
                            configKey, 
                            parentConfigKey,
                            tagName,
                            typeof(DefaultSection).AssemblyQualifiedName,
                            true,   // allowLocation
                            ConfigurationAllowDefinition.Everywhere,
                            ConfigurationAllowExeDefinition.MachineToRoamingUser,
                            true,   // restartOnExternalChanges
                            true,   // requirePermission
                            _flags[IsTrusted],
                            true,   // isUndeclared
                            null,
                            -1);

                    // Add any errors we may have encountered to the factory record,
                    // so that child config that also refer to this unrecognized section
                    // get the error.
                    factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));

                    // Add the factory to the list of factories
                    EnsureFactories()[configKey] = factoryRecord;
                }

                if (factoryRecord.IsGroup) {
                    //
                    // Section Group
                    //
                    if (factoryRecord.HasErrors) {
                        xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                    }
                    else {
                        if (xmlUtil.Reader.AttributeCount > 0) {
                            while (xmlUtil.Reader.MoveToNextAttribute()) {
                                if (IsReservedAttributeName(xmlUtil.Reader.Name)) {
                                    xmlUtil.AddErrorReservedAttribute(ExceptionAction.NonSpecific);
                                }
                            }

                            xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
                        }

                        // Recurse into group definition
                        ScanSectionsRecursive(xmlUtil, configKey, inLocation, locationSubPath, lockChildren, skipInChildApps);
                    }
                }
                else {
                    //
                    // Section
                    //
                    configKey = factoryRecord.ConfigKey;
                    string fileName = xmlUtil.Filename;
                    int lineNumber = xmlUtil.LineNumber;
                    string rawXml = null;
                    string configSource = null;
                    string configSourceStreamName = null;
                    object configSourceStreamVersion = null;
                    string protectionProviderName = null;
                    bool isSectionLocked = false;
                    bool positionedAtNextElement = false;
                    bool isFileInput = (locationSubPath == null);

                    if (!factoryRecord.HasErrors) {
                        // We have a valid factoryRecord for a section
                        if (inLocation && factoryRecord.AllowLocation == false) {
                            xmlUtil.SchemaErrors.AddError(
                                    new ConfigurationErrorsException(SR.GetString(SR.Config_section_cannot_be_used_in_location), xmlUtil), 
                                    ExceptionAction.Local);
                        }
    
                        // Verify correctness for file inputs.
                        if (isFileInput) {
                            // Verify that the section is unique
                            SectionRecord sectionRecord = GetSectionRecord(configKey, true);
                            if (sectionRecord != null && sectionRecord.HasFileInput) {
                                xmlUtil.SchemaErrors.AddError(
                                    new ConfigurationErrorsException(SR.GetString(SR.Config_sections_must_be_unique), xmlUtil),
                                    ExceptionAction.Local);
                            }

                            // Verify that the definition is allowed.
                            try {
                                VerifyDefinitionAllowed(factoryRecord, _configPath, xmlUtil);
                            }
                            catch (ConfigurationException ce) {
                                xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                            }
                        }

                        // Verify that section is unlocked, both for file and location inputs.
                        try {
                            VerifySectionUnlocked(configKey, xmlUtil);
                        }
                        catch (ConfigurationException ce) {
                            isSectionLocked = true;
                            xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                        }

                        // check for configSource or protectionProvider
                        if (xmlUtil.Reader.AttributeCount >= 1) {
                            // First do all the attributes reading without advancing the reader.
                            
                            string configSourceAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_CONFIGSOURCE);
                            if (configSourceAttribute != null) {
                                try {
                                    configSource = NormalizeConfigSource(configSourceAttribute, xmlUtil);
                                }
                                catch (ConfigurationException ce) {
                                    xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                }

                                if (xmlUtil.Reader.AttributeCount != 1) {
                                    // Error: elements with configSource should not have other attributes
                                    xmlUtil.SchemaErrors.AddError(
                                            new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil), 
                                            ExceptionAction.Local);
                                }
                            }

                            string protectionProviderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_PROTECTION_PROVIDER);
                            if (protectionProviderAttribute != null) {
                                try {
                                    protectionProviderName = ValidateProtectionProviderAttribute(protectionProviderAttribute, xmlUtil);
                                }
                                catch (ConfigurationException ce) {
                                    xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                }
                                
                                if (xmlUtil.Reader.AttributeCount != 1) {
                                    // Error: elements with protectionProvider should not have other attributes
                                    xmlUtil.SchemaErrors.AddError(
                                            new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_syntax_error), xmlUtil), 
                                            ExceptionAction.Local);
                                }
                            }

                            // The 2nd part of the configSource check requires advancing the reader.
                            // Please note that this part should be done only AFTER all other attributes
                            // checking are done.
                            if (configSourceAttribute != null) {
                                if (!xmlUtil.Reader.IsEmptyElement) {
                                    while (xmlUtil.Reader.Read()) {
                                        XmlNodeType t = xmlUtil.Reader.NodeType;
                                        if (t == XmlNodeType.EndElement)
                                            break;

                                        if (t != XmlNodeType.Comment) {
                                            // Error: elements with configSource should not subelements other than comments
                                            xmlUtil.SchemaErrors.AddError(
                                                    new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil),
                                                    ExceptionAction.Local);

                                            if (t == XmlNodeType.Element) {
                                                xmlUtil.StrictSkipToOurParentsEndElement(ExceptionAction.NonSpecific);
                                            }
                                            else {
                                                xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                                            }

                                            positionedAtNextElement = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        if (configSource != null) {
                            try {
                                try {
                                    configSourceStreamName = Host.GetStreamNameForConfigSource(ConfigStreamInfo.StreamName, configSource);
                                }
                                catch (Exception e) {
                                    throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_source_invalid), e, xmlUtil);
                                }
                                ValidateUniqueConfigSource(configKey, configSourceStreamName, configSource, xmlUtil);
                                configSourceStreamVersion = MonitorStream(configKey, configSource, configSourceStreamName);
                            }
                            catch (ConfigurationException ex) {
                                xmlUtil.SchemaErrors.AddError(ex, ExceptionAction.Local);
                            }
                        }

                        //
                        // prefetch the raw xml
                        //
                        if (!xmlUtil.SchemaErrors.HasLocalErrors) {
                            if (configSource == null && ShouldPrefetchRawXml(factoryRecord)) {
                                Debug.Assert(!positionedAtNextElement, "!positionedAtNextElement");

                                rawXml = xmlUtil.CopySection();
                                if (xmlUtil.Reader.NodeType != XmlNodeType.Element) {
                                    xmlUtil.VerifyIgnorableNodeType(ExceptionAction.NonSpecific);
                                    xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
                                }

                                positionedAtNextElement = true;
                            }
                        }
                    }

                    // Get the list of errors before advancing the reader
                    List<ConfigurationException> localErrors = xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(isFileInput);

                    // advance the reader to the next element
                    if (!positionedAtNextElement) {
                        xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);                            
                    }

                    // Add the input either to:
                    // 1. The file input at the current config level, or
                    // 2. LocationSections, where it will be used in sub paths
                    bool addInput = true;

                    if (isFileInput) {
                        // If isFileInput==true, Input added will be used against this config level.
                        // Need to check if we need to skip it due to inheritInChildApplications.
                        
                        if (ShouldSkipDueToInheritInChildApplications(skipInChildApps)) {
                            addInput = false;
                        }
                    }
                    else {
                        if (!_flags[SupportsLocation]) {
                            // Skip if we have a location input but we don't support location tag.
                            addInput = false;
                        }
                    }
                    
                    if (addInput) {

                        string targetConfigPath = (locationSubPath == null) ? _configPath : null;

                        SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
                                configKey, _configPath, targetConfigPath, locationSubPath,
                                fileName, lineNumber, ConfigStreamInfo.StreamVersion, rawXml, 
                                configSource, configSourceStreamName, configSourceStreamVersion,
                                protectionProviderName, lockChildren, skipInChildApps);

                        if (locationSubPath == null) {
                            //
                            // Add this file input to the section record
                            //

                            // We've already checked for locked above, so use skip the second check
                            // and set the locked bit.
                            SectionRecord sectionRecord = EnsureSectionRecordUnsafe(configKey, true);

                            Debug.Assert(!sectionRecord.Locked || isSectionLocked,
                                        "!sectionRecord.Locked || isSectionLocked");

                            if (isSectionLocked) {
                                sectionRecord.Locked = true;
                            }
                            
                            SectionInput fileInput = new SectionInput(sectionXmlInfo, localErrors);
                            sectionRecord.AddFileInput(fileInput);
                        }
                        else {
                            //
                            // Add this location input to this list of location sections
                            //
                            LocationSectionRecord locationSectionRecord = new LocationSectionRecord(sectionXmlInfo, localErrors);
                            EnsureLocationSections().Add(locationSectionRecord);
                        }
                    }
                }
            }
        }

        private void ScanLocationSection(XmlUtil xmlUtil) {
            string  locationSubPath   = null;
            bool    allowOverride     = true;
            bool    inheritInChildApp = true;
            int     errorCountBeforeScan = xmlUtil.SchemaErrors.GlobalErrorCount;

            // Get the location section attributes
            while (xmlUtil.Reader.MoveToNextAttribute()) {
                switch (xmlUtil.Reader.Name) {
                case KEYWORD_LOCATION_PATH:
                    locationSubPath = xmlUtil.Reader.Value;
                    break;

                case KEYWORD_LOCATION_ALLOWOVERRIDE:
                    xmlUtil.VerifyAndGetBooleanAttribute(
                            ExceptionAction.Global, true, out allowOverride);

                    break;

                case KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS:
                    xmlUtil.VerifyAndGetBooleanAttribute(
                            ExceptionAction.Global, true, out inheritInChildApp);

                    break;

                default:
                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Global);
                    break;
                }
            }

            xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element

            try {
                locationSubPath = NormalizeLocationSubPath(locationSubPath, xmlUtil);

                //
                //
                if (locationSubPath == null &&
                    !inheritInChildApp &&
                    Host.IsDefinitionAllowed(_configPath, ConfigurationAllowDefinition.MachineToWebRoot, ConfigurationAllowExeDefinition.MachineOnly)) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Location_invalid_inheritInChildApplications_in_machine_or_root_web_config), xmlUtil);
                }
            }
            catch (ConfigurationErrorsException ce) {
                xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Global);
            }
       
            // Skip over this location section if there are errors
            if (xmlUtil.SchemaErrors.GlobalErrorCount > errorCountBeforeScan) {
                xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                return;
            }

            // Scan elements of the location section if the path is the current path.
            // We do not add <location path="." /> to the _locationSections list.
            if (locationSubPath == null) {
                ScanSectionsRecursive(xmlUtil, string.Empty, true, null, !allowOverride, !inheritInChildApp);
                return;
            }

            // Skip over location sections for client config
            if (!_flags[SupportsLocation]) {
                xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                return;
            }

            AddLocation(locationSubPath);
            ScanSectionsRecursive(xmlUtil, string.Empty, true, locationSubPath, !allowOverride, !inheritInChildApp);
        }


        // AddLocation
        //
        // If you wish to keep track of the Location Fields, then use this
        //
        protected virtual void AddLocation(string LocationSubPath) {}
        
        //
        // Resolve information about a location section at the time that the location section
        // is being used by child configuration records. This allows us to:
        //      * Delay determining the configuration path for the location record until the sites section is available.
        //      * Delay reporting bad location paths until the location record has to be used.
        //
        private void ResolveLocationSections() {
            if (!_flags[IsLocationListResolved]) {
                // Resolve outside of any lock
                if (!_parent.IsRootConfig) {
                    _parent.ResolveLocationSections();
                }
                
                lock (this) {
                    if (!_flags[IsLocationListResolved]) {
                        if (_locationSections != null) {
                            //
                            // Create dictionary that maps configPaths to (dictionary that maps sectionNames to locationSectionRecords)
                            //
                            HybridDictionary locationConfigPaths = new HybridDictionary(true);
                            foreach (LocationSectionRecord locationSectionRecord in _locationSections) {
                                //
                                // Resolve the target config path
                                //
                                string targetConfigPath = Host.GetConfigPathFromLocationSubPath(_configPath, locationSectionRecord.SectionXmlInfo.SubPath);
                                locationSectionRecord.SectionXmlInfo.TargetConfigPath = targetConfigPath;
                                    
                                //
                                // Check uniqueness
                                //
                                HybridDictionary locationSectionRecordDictionary = (HybridDictionary) locationConfigPaths[targetConfigPath];
                                if (locationSectionRecordDictionary == null) {
                                    locationSectionRecordDictionary = new HybridDictionary(false);
                                    locationConfigPaths.Add(targetConfigPath, locationSectionRecordDictionary);
                                }

                                LocationSectionRecord duplicateRecord = (LocationSectionRecord) locationSectionRecordDictionary[locationSectionRecord.ConfigKey];
                                if (duplicateRecord == null) {
                                    locationSectionRecordDictionary.Add(locationSectionRecord.ConfigKey, locationSectionRecord);
                                }
                                else {
                                    if (!duplicateRecord.HasErrors) {
                                        duplicateRecord.AddError(
                                            new ConfigurationErrorsException(
                                                    SR.GetString(SR.Config_sections_must_be_unique), 
                                                    duplicateRecord.SectionXmlInfo));
                                    }

                                    locationSectionRecord.AddError(
                                            new ConfigurationErrorsException(
                                                    SR.GetString(SR.Config_sections_must_be_unique), 
                                                    locationSectionRecord.SectionXmlInfo));
                                }

                                //
                                // Check if the definition is allowed
                                //
                                FactoryRecord factoryRecord = FindFactoryRecord(locationSectionRecord.ConfigKey, true);
                                if (!factoryRecord.HasErrors) {
                                    try {
                                        VerifyDefinitionAllowed(factoryRecord, targetConfigPath, locationSectionRecord.SectionXmlInfo);
                                    }
                                    catch (ConfigurationException e) {
                                        locationSectionRecord.AddError(e);
                                    }
                                }
                            }

                            //
                            // Check location section for being locked.
                            //
                            BaseConfigurationRecord parent = _parent;
                            while (!parent.IsRootConfig) {
                                foreach (LocationSectionRecord locationSectionRecord in this._locationSections) {
                                    bool locked = false;

                                    //
                                    // It is an error if a parent section with the same configKey is locked.
                                    //
                                    SectionRecord sectionRecord = parent.GetSectionRecord(locationSectionRecord.ConfigKey, true);
                                    if (   sectionRecord != null && 
                                            (sectionRecord.LockChildren || sectionRecord.Locked)) {

                                        locked = true;
                                    }
                                    else {
                                        //
                                        // It is an error if a parent configuration file locks a section for the
                                        // locationConfigPath or any sub-path of the locationConfigPath.
                                        //
                                        if (parent._locationSections != null) {
                                            string targetConfigPath = locationSectionRecord.SectionXmlInfo.TargetConfigPath;

                                            foreach (LocationSectionRecord parentLocationSectionRecord in parent._locationSections) {
                                                string parentTargetConfigPath = parentLocationSectionRecord.SectionXmlInfo.TargetConfigPath;

                                                if (   parentLocationSectionRecord.SectionXmlInfo.LockChildren &&
                                                       locationSectionRecord.ConfigKey == parentLocationSectionRecord.ConfigKey &&
                                                       UrlPath.IsEqualOrSubpath(targetConfigPath, parentTargetConfigPath)) {

                                                    locked = true;
                                                    break;
                                                }
                                            }
                                        }
                                    }

                                    if (locked) {
                                        locationSectionRecord.AddError(new ConfigurationErrorsException(
                                            SR.GetString(SR.Config_section_locked),
                                            locationSectionRecord.SectionXmlInfo));
                                    }
                                }

                                parent = parent._parent;
                            }
                        }
                    }

                    _flags[IsLocationListResolved] = true;
                }
            }
        }

        // VerifyDefinitionAllowed
        //
        // Verify that the Definition is allowed at this 
        // place.
        //
        // For example, if this config record is an application then 
        // make sure the section say's it can be defined in an
        // application
        //
        private void VerifyDefinitionAllowed(FactoryRecord factoryRecord, string configPath, IConfigErrorInfo errorInfo) {
            Host.VerifyDefinitionAllowed(configPath, factoryRecord.AllowDefinition, factoryRecord.AllowExeDefinition, errorInfo);
        }

        internal bool IsDefinitionAllowed(ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition) {
            return Host.IsDefinitionAllowed(_configPath, allowDefinition, allowExeDefinition);
        }

        static protected void VerifySectionName(string name, XmlUtil xmlUtil, ExceptionAction action, bool allowImplicit) {
            try {
                VerifySectionName(name, (IConfigErrorInfo) xmlUtil, allowImplicit);
            }
            catch (ConfigurationErrorsException ce) {
                xmlUtil.SchemaErrors.AddError(ce, action);
            }
        }

        // Check if the section name contains reserved words from the config system,
        // and is a valid name for an XML Element.
        static protected void VerifySectionName(string name, IConfigErrorInfo errorInfo, bool allowImplicit) {
            if (String.IsNullOrEmpty(name)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_invalid), errorInfo);
            }

            // must be a valid name in xml, so that it can be used as an element
            // n.b. - it also excludes forward slash '/'
            try {
                XmlConvert.VerifyName(name);
            }
            // Do not let the exception propagate as an XML exception,
            // for we want errors in the section name to be treated as local errors,
            // not global ones.
            catch (Exception e) {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_tag_name_invalid), e, errorInfo);
            }
            catch {
                throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_tag_name_invalid), null, errorInfo);
            }

            if (IsImplicitSection(name)) {
                if (allowImplicit) {
                    // avoid test below for strings starting with "config"
                    return;
                }
                else {
                    throw new ConfigurationErrorsException(SR.GetString(SR.Cannot_declare_or_remove_implicit_section, name), errorInfo);
                }
            }

            if (StringUtil.StartsWith(name, "config")) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_cannot_begin_with_config), errorInfo);
            }

            if (name == KEYWORD_LOCATION) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_cannot_be_location), errorInfo);
            }
        }

        /*
        From http://www.w3.org/Addressing/

          reserved    = ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ','

        From Platform SDK

          reserved    = '\' |  '/' | '|' | ':' |  '"' |  '<' | '>' 

        */

        // NOTE: If you change these strings, you must change the associated error message
        const string invalidFirstSubPathCharacters  = @"\./";
        const string invalidLastSubPathCharacters   = @"\./";
        const string invalidSubPathCharactersString = @"\;?:@&=+$,*""<>|";
        static char[] s_invalidSubPathCharactersArray = invalidSubPathCharactersString.ToCharArray();

        // Return null if the subPath represents the current directory, for example:
        //      path=""
        //      path="   "
        //      path="."
        //      path="./"
        internal static string NormalizeLocationSubPath(string subPath, IConfigErrorInfo errorInfo) {
            // if subPath is null or empty, it is the current dir
            if (String.IsNullOrEmpty(subPath))
                return null;

            // if subPath=".", it is the current dir
            if (subPath == ".")
                return null;

            // do not allow whitespace in front of subPath, as the OS
            // handles beginning and trailing whitespace inconsistently
            string trimmedSubPath = subPath.TrimStart();
            if (trimmedSubPath.Length != subPath.Length) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_first_character), errorInfo);
            }

            // do not allow problematic starting characters
            if (invalidFirstSubPathCharacters.IndexOf(subPath[0]) != -1) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_first_character), errorInfo);
            }

            // do not allow whitespace at end of subPath, as the OS
            // handles beginning and trailing whitespace inconsistently
            trimmedSubPath = subPath.TrimEnd();
            if (trimmedSubPath.Length != subPath.Length) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_last_character), errorInfo);
            }

            // the file system ignores trailing '.', '\', or '/', so do not allow it in a location subpath specification
            if (invalidLastSubPathCharacters.IndexOf(subPath[subPath.Length-1]) != -1) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_last_character), errorInfo);
            }

            // combination of URI reserved characters and OS invalid filename characters, minus / (allowed reserved character)
            if (subPath.IndexOfAny(s_invalidSubPathCharactersArray) != -1) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_character), errorInfo);
            }

            return subPath;
        }


        //
        // Return the SectionRecord for a section.
        // If the record does not exist, return null.
        // Throw cached errors if the section is in error and permitErrors == false.
        //
        protected SectionRecord GetSectionRecord(string configKey, bool permitErrors) {
            SectionRecord sectionRecord;

            if (_sectionRecords != null) {
                sectionRecord = (SectionRecord) _sectionRecords[configKey];
            }
            else {
                sectionRecord = null;
            }

            if (sectionRecord != null && !permitErrors) {
                sectionRecord.ThrowOnErrors();
            }

            return sectionRecord;
        }

        // Return an existing SectionRecord, or create one if one does not exist.
        // Propagate the Locked bit from parent
        protected SectionRecord EnsureSectionRecord(string configKey, bool permitErrors) {
            return EnsureSectionRecordImpl(configKey, permitErrors, true);
        }

        // Return an existing SectionRecord, or create one if one does not exist.
        // Do not propagate the Locked bit from parent, because caller will check
        // himself later.
        protected SectionRecord EnsureSectionRecordUnsafe(string configKey, bool permitErrors) {
            return EnsureSectionRecordImpl(configKey, permitErrors, false);
        }

        // Return an existing SectionRecord, or create one if one does not exist.
        // If desired, set the locked bit if locked by a parent.
        private SectionRecord EnsureSectionRecordImpl(string configKey, bool permitErrors, bool checkIfLocked) {
            SectionRecord sectionRecord = GetSectionRecord(configKey, permitErrors);
            if (sectionRecord == null) {
                bool sectionAdded = false;
                lock (this) {
                    if (_sectionRecords == null) {
                        _sectionRecords = new Hashtable();
                    }
                    else {
                        sectionRecord = GetSectionRecord(configKey, permitErrors);
                    }

                    if (sectionRecord == null) {
                        sectionRecord = new SectionRecord(configKey);
                        _sectionRecords.Add(configKey, sectionRecord);
                        sectionAdded = true;
                    }
                }

                if (checkIfLocked && sectionAdded) {
                    sectionRecord.Locked = IsParentCausingLock(configKey);
                }
            }

            return sectionRecord;
        }

        private bool HasFactoryRecords {
            get {
                return _factoryRecords != null;
            }
        }

        internal FactoryRecord GetFactoryRecord(string configKey, bool permitErrors) {
            FactoryRecord factoryRecord;

            if (_factoryRecords == null) {
                return null;
            }

            factoryRecord = (FactoryRecord) _factoryRecords[configKey];
            if (factoryRecord != null && !permitErrors) {
                factoryRecord.ThrowOnErrors();
            }

            return factoryRecord;
        }
        
        // Only create a _factories hashtable when necessary.
        // Most config records won't have factories, so we can save 120 bytes
        // per record by creating the table on demand.
        protected Hashtable EnsureFactories() {
            if (_factoryRecords == null) {
                _factoryRecords = new Hashtable();
            }

            return _factoryRecords;
        }

        private ArrayList EnsureLocationSections() {
            if (_locationSections == null) {
                _locationSections = new ArrayList();
            }

            return _locationSections;
        }

        // Return true if there is no unique configuration information in this record.
        internal bool IsEmpty {
            get {
                return 
                        _parent != null
                    && !_initErrors.HasErrors(false)
                    && (_sectionRecords == null   || _sectionRecords.Count == 0)
                    && (_factoryRecords == null   || _factoryRecords.Count == 0)
                    && (_locationSections == null || _locationSections.Count == 0);
            }
        }

        static internal string NormalizeConfigSource(string configSource, IConfigErrorInfo errorInfo) {
            if (String.IsNullOrEmpty(configSource)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
            }

            string trimmedConfigSource = configSource.Trim();
            if (trimmedConfigSource.Length != configSource.Length) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
            }

            if (configSource.IndexOf('/') != -1) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_chars), errorInfo);
            }
            
            if (String.IsNullOrEmpty(configSource) || System.IO.Path.IsPathRooted(configSource)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
            }

            return configSource;
        }

        protected object MonitorStream(string configKey, string configSource, string streamname) {
            lock (this) {
                if (_flags[Closed]) {
                    return null;
                }

                StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
                if (streamInfo != null) {
                    if (streamInfo.SectionName != configKey) {
                        throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_cannot_be_shared, streamname));
                    }

                    if (streamInfo.IsMonitored) {
                        return streamInfo.Version;
                    }
                }
                else {
                    streamInfo = new StreamInfo(configKey, configSource, streamname);
                    ConfigStreamInfo.StreamInfos.Add(streamname, streamInfo);
                }
            }

            //
            // Call the host outside the lock to avoid deadlock.
            //
            object version = Host.GetStreamVersion(streamname);

            StreamChangeCallback callbackDelegate = null;

            lock (this) {
                if (_flags[Closed]) {
                    return null;
                }

                StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
                if (streamInfo.IsMonitored) {
                    return streamInfo.Version;
                }

                streamInfo.IsMonitored = true;
                streamInfo.Version = version;

                if (_flags[SupportsChangeNotifications]) {
                    if (ConfigStreamInfo.CallbackDelegate == null) {
                        ConfigStreamInfo.CallbackDelegate = new StreamChangeCallback(this.OnStreamChanged);
                    }

                    callbackDelegate = ConfigStreamInfo.CallbackDelegate;
                }
            }

            if (_flags[SupportsChangeNotifications]) {
                Host.StartMonitoringStreamForChanges(streamname, callbackDelegate);
            }

            return version;
        }

        private void OnStreamChanged(string streamname) {
            bool        notifyChanged;
            StreamInfo  streamInfo;
            string      sectionName;

            lock (this) {
                if (_flags[Closed])
                    return;

                streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
                if (streamInfo == null || !streamInfo.IsMonitored)
                    return;

                sectionName = streamInfo.SectionName;
            }

            if (sectionName == null) {
                notifyChanged = true;
            }
            else {
                FactoryRecord factoryRecord = FindFactoryRecord(sectionName, false);
                notifyChanged = factoryRecord.RestartOnExternalChanges;
            }

            if (notifyChanged) {
                _configRoot.FireConfigChanged(_configPath);
            }
            else {
                _configRoot.ClearResult(this, sectionName, false);
            }
        }

        //
        //
        private void ValidateUniqueConfigSource(
                string configKey, string configSourceStreamName, string configSourceArg, IConfigErrorInfo errorInfo) {

            //
            // Detect if another section in this file is using the same configSource 
            // with has a different section name.
            //
            lock (this) {
                if (ConfigStreamInfo.HasStreamInfos) {
                    StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[configSourceStreamName];
                    if (streamInfo != null && streamInfo.SectionName != configKey) {
                        throw new ConfigurationErrorsException(
                            SR.GetString(SR.Config_source_cannot_be_shared, configSourceArg),
                            errorInfo);
                    }
                }
            }
            
            ValidateUniqueChildConfigSource(configKey, configSourceStreamName, configSourceArg, errorInfo);
        }
            
        protected void ValidateUniqueChildConfigSource(
                string configKey, string configSourceStreamName, string configSourceArg, IConfigErrorInfo errorInfo) {

            //
            // Detect if a parent config file is using the same config source stream.
            //
            BaseConfigurationRecord current;
            if (IsLocationConfig) {
                current = _parent._parent;
            }
            else {
                current = _parent;
            }

            while (!current.IsRootConfig) {
                lock (current) {
                    if (current.ConfigStreamInfo.HasStreamInfos) {
                        StreamInfo streamInfo = (StreamInfo) current.ConfigStreamInfo.StreamInfos[configSourceStreamName];
                        if (streamInfo != null) {
                            throw new ConfigurationErrorsException(
                                SR.GetString(SR.Config_source_parent_conflict, configSourceArg),
                                errorInfo);
                        }
                    }
                }

                current = current.Parent;
            }
        }

        // Recursively clear the result.
        // If forceEvaluation == true, force a rescan of the config file to find
        // the section.
        // Requires the hierarchy lock to be acquired (hl)
        internal void hlClearResultRecursive(string configKey, bool forceEvaluatation) {
            SectionRecord sectionRecord;

            // Refresh it's factory Record
            RefreshFactoryRecord(configKey);

            // Clear any stored result in the section
            sectionRecord = GetSectionRecord(configKey, false);
            if (sectionRecord != null) {
                sectionRecord.ClearResult();

                sectionRecord.ClearRawXml();
            }

            //
            // If we need to reevaluate, add a dummy file input so 
            // that we open the file on the next evaluation
            //
            if (forceEvaluatation && !IsInitDelayed && !String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
                if (_flags[SupportsPath]) {
                    throw ExceptionUtil.UnexpectedError("BaseConfigurationRecord::hlClearResultRecursive");
                }

                FactoryRecord factoryRecord = FindFactoryRecord(configKey, false);
                if (factoryRecord != null && !factoryRecord.IsGroup) {
                    configKey = factoryRecord.ConfigKey;
                    sectionRecord = EnsureSectionRecord(configKey, false);
                    if (!sectionRecord.HasFileInput) {
                        SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
                            configKey, _configPath, _configPath, null,
                            ConfigStreamInfo.StreamName, 0, null, null, 
                            null, null, null, 
                            null, false, false);

                        SectionInput fileInput = new SectionInput(sectionXmlInfo, null);
                        sectionRecord.AddFileInput(fileInput);
                    }
                }
            }

            // Recurse
            if (_children != null) {
                IEnumerable children = _children.Values;
                foreach (BaseConfigurationRecord child in children) {
                    child.hlClearResultRecursive(configKey, forceEvaluatation);
                }
            }
        }

        // Returns a child record.
        // Requires the hierarchy lock to be acquired (hl)
        internal BaseConfigurationRecord hlGetChild(string configName) {
            if (_children == null)
                return null;

            return (BaseConfigurationRecord) _children[configName];
        }

        // Adds a child record.
        // Requires the hierarchy lock to be acquired (hl)
        internal void hlAddChild(string configName, BaseConfigurationRecord child) {
            if (_children == null) {
                _children = new Hashtable(StringComparer.OrdinalIgnoreCase);
            }

            _children.Add(configName, child);
        }

        // Removes a child record.
        // Requires the hierarchy lock to be acquired (hl)
        internal void hlRemoveChild(string configName) {
            if (_children != null) {
                _children.Remove(configName);
            }
        }

        // Removes true if a child record is needed for a 
        // child config path.
        // Requires the hierarchy lock to be acquired (hl)
        internal bool hlNeedsChildFor(string configName) {
            // Always return true for root config record
            if (IsRootConfig)
                return true;

            // Never create a child record when the parent has an exception.
            if (HasInitErrors) {
                return false;
            }

            string childConfigPath = ConfigPathUtility.Combine(_configPath, configName);

            try {
                using (Impersonate()) {
                    // check host if required
                    if (Host.IsConfigRecordRequired(childConfigPath)) {
                        return true;
                    }
                }
            }
            catch {
                // Don't allow frames up the stack to run exception filters while impersonated.
                throw;
            }

            // see if there's a location
            if (_flags[SupportsLocation]) {
                BaseConfigurationRecord configRecord = this;

                while (!configRecord.IsRootConfig) {
                    if (configRecord._locationSections != null) {
                        configRecord.ResolveLocationSections();
                        foreach (LocationSectionRecord locationSectionRecord in configRecord._locationSections) {
                            if (UrlPath.IsEqualOrSubpath(locationSectionRecord.SectionXmlInfo.TargetConfigPath, childConfigPath)) {
                                return true;
                            }
                        }
                    }

                    configRecord = configRecord._parent;
                }
            }

            return false;
        }

        // Close the record. An explicit close is needed 
        // in order to stop monitoring streams used by 
        // this record. Stream monitors cause this record
        // to be rooted in the GC heap.
        //
        // Note that we purposely do not cleanup the child/parent
        // hierarchy. This is so that a config system which has
        // a pointer to this record can still call GetSection on
        // it while another thread closes it.
        internal void CloseRecursive() {
            if (!_flags[Closed]) {
                bool doClose = false;
                HybridDictionary streamInfos = null;
                StreamChangeCallback callbackDelegate = null;

                lock (this) {
                    if (!_flags[Closed]) {
                        _flags[Closed] = true;
                        doClose = true;

                        if (!IsLocationConfig && ConfigStreamInfo.HasStreamInfos) {
                            callbackDelegate = ConfigStreamInfo.CallbackDelegate;
                            streamInfos = ConfigStreamInfo.StreamInfos;

                            ConfigStreamInfo.CallbackDelegate = null;
                            ConfigStreamInfo.ClearStreamInfos();
                        }
                    }
                }

                if (doClose) {
                    // no hierarchy lock is needed to access _children here,
                    // as it has already been detached from the hierarchy tree
                    if (_children != null) {
                        foreach (BaseConfigurationRecord child in _children.Values) {
                            child.CloseRecursive();
                        }
                    }

                    if (streamInfos != null) {
                        foreach (StreamInfo streamInfo in streamInfos.Values) {
                            if (streamInfo.IsMonitored) {
                                Host.StopMonitoringStreamForChanges(streamInfo.StreamName, callbackDelegate);
                                streamInfo.IsMonitored = false;
                            }
                        }
                    }
                }
            }
        }

        internal string FindChangedConfigurationStream() {
            BaseConfigurationRecord configRecord = this;
            while (!configRecord.IsRootConfig) {
                lock (configRecord) {
                    if (configRecord.ConfigStreamInfo.HasStreamInfos) {
                        foreach (StreamInfo streamInfo in configRecord.ConfigStreamInfo.StreamInfos.Values) {
                            if (streamInfo.IsMonitored && HasStreamChanged(streamInfo.StreamName, streamInfo.Version)) {
                                return streamInfo.StreamName;
                            }
                        }
                    }
                }

                configRecord = configRecord._parent;
            }

            return null;
        }

        private bool HasStreamChanged(string streamname, object lastVersion) {
            object currentVersion = Host.GetStreamVersion(streamname);

            if (lastVersion != null) {
                return (currentVersion == null || !lastVersion.Equals(currentVersion));
            }
            else {
                return currentVersion != null;
            }
        }

        protected virtual string CallHostDecryptSection(string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfig) {
            return Host.DecryptSection(encryptedXml, protectionProvider, protectedConfig);
        }

        static internal string ValidateProtectionProviderAttribute(string protectionProvider, IConfigErrorInfo errorInfo) {
            if (String.IsNullOrEmpty(protectionProvider)) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_invalid_format), errorInfo);
            }

            return protectionProvider;
        }

        private ConfigXmlReader DecryptConfigSection(ConfigXmlReader reader, ProtectedConfigurationProvider protectionProvider) {
            ConfigXmlReader     clone           = reader.Clone();
            IConfigErrorInfo    err             = (IConfigErrorInfo)clone;
            string              encryptedXml    = null;
            string              clearTextXml    = null;
            XmlNodeType         nodeType;

            clone.Read();

            // Save the file and line at the top of the section
            
            string filename = err.Filename;
            int lineNumber = err.LineNumber;
            int sectionLineNumber = lineNumber;

            if (clone.IsEmptyElement) {
                throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_not_found), filename, lineNumber);
            }

            //////////////////////////////////////////////////////////
            // Find the <EncryptedData> node
            for (;;) {
                clone.Read(); // Keep reading till we find a relavant node
                
                nodeType = clone.NodeType;
                
                if (nodeType  == XmlNodeType.Element && clone.Name == "EncryptedData")  { // Found it!
                    break;
                }

                if (nodeType == XmlNodeType.EndElement) {
                    throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_not_found), filename, lineNumber);
                }
                else if (nodeType  != XmlNodeType.Comment && nodeType != XmlNodeType.Whitespace) {
                    // some other unexpected content
                    throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_is_in_invalid_format), filename, lineNumber);
                }
            }

            //////////////////////////////////////////////////////////
            // Do the decryption

            // Save the line at the top of the <EncryptedData> node
            lineNumber = err.LineNumber;

            encryptedXml = clone.ReadOuterXml();
            try {
                clearTextXml = CallHostDecryptSection(encryptedXml, protectionProvider, ProtectedConfig);
            } catch (Exception e) {
                throw new ConfigurationErrorsException(SR.GetString(SR.Decryption_failed, protectionProvider.Name, e.Message), e, filename, lineNumber);
            }
            catch {
                throw new ConfigurationErrorsException(SR.GetString(SR.Decryption_failed, protectionProvider.Name, ExceptionUtil.NoExceptionInformation), filename, lineNumber);
            }

            // Detect if there is any XML left over after <EncryptedData>
            do {
                nodeType = clone.NodeType;
                
                if (nodeType == XmlNodeType.EndElement) {
                    break;
                }
                else if (nodeType  != XmlNodeType.Comment && nodeType != XmlNodeType.Whitespace) {
                    // Got other unexpected content
                    throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_is_in_invalid_format), filename, lineNumber);
                }
            } while (clone.Read());
            
            // Create a new reader, using the position of the original reader
            return new ConfigXmlReader(clearTextXml, filename, sectionLineNumber, true);
        }

        // ConfigContext
        //
        // Retrieve the context for the config
        //
        internal object ConfigContext
        {
            get
            {
                if (!_flags[ContextEvaluated]) {
                    // Retrieve context for Path
                    _configContext = Host.CreateConfigurationContext(ConfigPath, LocationSubPath);
                    _flags[ContextEvaluated] = true;
                }
                
                return _configContext;
            }
        }

        // ThrowIfParseErrors
        //
        // Throw if there were parse errors detected
        //
        private void ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors) {
            schemaErrors.ThrowIfErrors(ClassFlags[ClassIgnoreLocalErrors]);
        }


        // RecordSupportsLocation
        //
        // Does it make sense to put use location tags in this file?
        // In the web case this is true at any level.  In the exe case
        // this is only true for machine.config (since machine.config
        // can really be used for any scenario)
        //
        internal bool RecordSupportsLocation {
            get {
                return (_flags[SupportsLocation] || IsMachineConfig);
            }
        }

        //
        // Note: Some of the per-attribute encryption stuff is moved to the end of the file to minimize
        //       FI merging conflicts
        //
        const string ProtectedConfigurationSectionTypeName = "System.Configuration.ProtectedConfigurationSection, " + AssemblyRef.SystemConfiguration;
        internal const string RESERVED_SECTION_PROTECTED_CONFIGURATION       = "configProtectedData";

        internal static bool IsImplicitSection(string configKey) {
            if (configKey == RESERVED_SECTION_PROTECTED_CONFIGURATION) {
                return true;
            }
            else {
                return false;
            }
        }

        //
        // Add implicit sections to the factory list.
        // If factoryList == null, then add to the config record's factory list.
        //
        private void AddImplicitSections(Hashtable factoryList) {
            // Add implicit sections to the factoryList if we're under the root
            // (e.g. if we're in machine.config)
            if (_parent.IsRootConfig
                ) {
                
                if (factoryList == null) {
                    factoryList = EnsureFactories();
                }

                FactoryRecord factoryRecord = (FactoryRecord)factoryList[RESERVED_SECTION_PROTECTED_CONFIGURATION];
                
                // If the user has mistakenly declared an implicit section, we should leave the factoryRecord
                // alone because it contains the error and the error will be thrown later.
                if (factoryRecord != null) {
                    Debug.Assert(factoryRecord.HasErrors, "If the user has mistakenly declared an implicit section, we should have recorded an error.");
                }
                else {
                    factoryList[RESERVED_SECTION_PROTECTED_CONFIGURATION] = 
                        new FactoryRecord(
                                RESERVED_SECTION_PROTECTED_CONFIGURATION,   // configKey
                                string.Empty,                               // group
                                RESERVED_SECTION_PROTECTED_CONFIGURATION,   // name
                                ProtectedConfigurationSectionTypeName,      // factoryTypeName
                                true,                                       // allowLocation
                                ConfigurationAllowDefinition.Everywhere,    // allowDefinition
                                ConfigurationAllowExeDefinition.MachineToApplication,   // allowExeDefinition
                                true,                                       // restartOnExternalChanges
                                true,                                       // requirePermission
                                true,                                       // isFromTrustedConfig
                                true,                                       // isUndeclared
                                null,                                       // filename
                                -1);                                        // lineNumber
                }
            }
        }

        // We reserve all attribute names starting with config or lock
        internal static bool IsReservedAttributeName(string name) {
            if (StringUtil.StartsWith(name, "config") ||
                StringUtil.StartsWith(name, "lock")) {
                return true;
            }
            else {
                return false;
            }
        }

        protected class ConfigRecordStreamInfo {
            private bool                    _hasStream;             // does the stream exist?
            private string                  _streamname;            // name of the stream of this record
            private object                  _streamVersion;         // version of the stream
            private Encoding                _encoding;              // encoding of the stream
            private StreamChangeCallback    _callbackDelegate;      // host delegate to callback to when stream has changed
            private HybridDictionary        _streamInfos;           // streamname -> StreamInfo. It'll also contain the main stream pointed to by _streamname

            internal ConfigRecordStreamInfo() {
                // default encoding
                _encoding = Encoding.UTF8;
            }

            internal bool HasStream {
                get { return _hasStream; }
                set { _hasStream = value; }
            }

            internal string StreamName {
                get { return _streamname; }
                set { _streamname = value; }
            }

            internal object StreamVersion {
                get { return _streamVersion; }
                set { _streamVersion = value; }
            }

            internal Encoding StreamEncoding {
                get { return _encoding; }
                set { _encoding = value; }
            }

            internal StreamChangeCallback CallbackDelegate {
                get { return _callbackDelegate; }
                set { _callbackDelegate = value; }
            }

            internal HybridDictionary StreamInfos {
                get { 
                    if (_streamInfos == null) {
                        _streamInfos = new HybridDictionary(true);
                    }

                    return _streamInfos; 
                }
            }

            internal bool HasStreamInfos {
                get { return _streamInfos != null; }
            }

            internal void ClearStreamInfos() {
                _streamInfos = null;
            }

#if DBG
            // For Debugging only
            internal string[] Keys {
                get {
                    string[]    keys = new string[StreamInfos.Count];
                    StreamInfos.Keys.CopyTo(keys, 0);
                    return keys;
                }
            }
#endif      
        }
    }
}
